作用域
- 全局作用域
- 函数作用域
- 块级作用域(es6)
let
- let 声明的变量只在所处的块级有效
- let没有变量声明提升
需要注意的一点:
for (let i = 0;i < 3; i++) {
let i = "xxx"
console.log(i);
}
其实在此处是两层的块级作用域
const
- const 与 let一样作用域都是块级
- const只用来声明常量
- const声明的时候必须复制,否则会报错
const obj = {}
obj.name = "sam"
这种情况不会报错,根本原因是obj
指向的地址是没变的
数组的解构
const [first, second, third] = [1,2,3]
const [first, second, third, fourth = 4] = [1,2,3]
const [first, ...rest] = [1,2,3,4]
对象的解构
const obj = {name: "xxx", age: 18}
const {name,age} = obj
模板字符串
const param = "world"
const str = `hello, //可换行
${param}` //嵌入变量以及script语句
console.log(str)
还一个比较特殊的使用方式是带标签
const name = "sam"
function tagFn (string, name) {
console.log(string, name) // 可以在这部分做对字符串的修饰
return string[0] + name + string[1]
}
const result = tagFn`hi, ${name}.`
console.log(result)
字符串扩展方法
includes()
startsWith()
endsWith()
参数默认值
function foo (a = 1) {
console.log(a )
}
foo()
剩余参数
function foo (...rest) { //只能用在形参的最后一位 并且只能使用一次
console.log(rest)
}
foo(1,2,3,4)
展开数组
const arr = [1,2,3]
console.log.apply(console, arr)//两种方式等同
console.log(...arr)
箭头函数
const inc = (n,m) => {
return n + m
}
console.log(inc(1,2))
箭头函数不会改变this的指向
对象字面量的增强
const bar = "2"
const obj = {
foo: 1,
bar,
fn() {
console.log("fnfnfnfnfnfn")
}
}
obj[1+1] = 3
obj.fn()
对象的扩展方法
Object.assign()
const obj1 = {
a:1,
b:2
}
const obj2 = {
a:3,
c:4
}
const obj = Object.assign(obj1, obj2)
console.log(obj)
console.log(obj === obj1)
//{ a: 3, b: 2, c: 4 }
//true
Object.is()
Object.is(+0 === -0)
Proxy
Proxy
在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作必须通过这层拦截
const person = {
name: "sam",
age: 18
}
const personProxy = new Proxy(person, {
get(target, property) {
return property in target ? target[property] : "default"
},
set(target, property, value) {
if (property === "age") {
if (!Number.isInteger(value)){
throw new TypeError(`${value} is not an int`)
}
}
target[property] = value
}
})
personProxy.age = 12
personProxy.sex = "man"
我们发现proxy
与Object.defineProperty()
的作用相似,那么两者有什么区别呢?
-
Object.defineProperty()
只能监控到对象属性的读写 -
proxy
可监控更多操作例如属性删除,对象方法调用
handle | 触发条件 |
---|---|
get | 读取某个属性 |
set | 写入某个属性 |
has | in 操作符 |
deleteProperty | delete 操作符 |
getProperty | Object.getPropertypeOf() |
setProperty | Object.setPrototypeOf() |
isExtensible | Object.isExtensible() |
preventExtensions | Object.preventExtensions() |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() |
defineProperty | Object.defineProperty() |
ownKeys | Object.keys() 、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols() |
apply | 调用一个函数 |
construct | 用 new 调用一个函数 |
对数组监视
本质: 重写数组的操作方法。
用proxy
监视数组
const list = []
const listProxy = new Proxy(list, {
set(target, property, value) {
console.log("push", property, value)
target[property] = value
return true
}
})
listProxy.push(100)
非侵入便可监视
Reflect
Reflect
是一个静态类,使用方式类似Math
Reflect
成员方法就是Proxy
处理对象的默认实现。
Reflect
提供了一套用于操作对象的API,详情见以上表格
const obj = {
name: "zicoo",
age: 18
}
// console.log("name" in obj)
console.log(Reflect.has(obj, "name"))
// console.log(delete obj[age])
console.log(Reflect.deleteProperty(obj, "age"))
// console.log(Object.keys(obj))
console.log(Reflect.ownKeys(obj))
Promise
提供了一种更优的异步编程方案
具体可见之前的文章《Promise核心功能从原理到实现》
class 类
class Person {
constructor (name) {
this.name = name
}
say() {
console.log(`my name is ${this.name}`)
}
static create(name) {
return new Person(name)
}
}
const zicoo = new Person("zicoo")
zicoo.say()
const sam = Person.create("sam")
sam.say()
class Student extends Person {
constructor(name, number) {
super(name)
this.number = number
}
hello() {
super.say()
console.log("student say hello")
}
}
const tom = new Student("tom", 10)
tom.hello()
Set 数据结构
- 可看做集合
-
Set
中数据不重复
const s = new Set();
s.add(1).add(2).add(1)
console.log(s)
//s.forEach()
//for (let i of s)
// s.zie
// s.has(1)
// s.delete(2)
// s.clear()
Set
有个常见的场景是用来数组去重
const arr = [1,2,3,1,3,2]
const result = Array.from(new Set(arr))
// const result = [...new Set(arr)]
console.log(result)
Map数据结构
与Object
类似都是存的键值对,但是Object的key
只能为String
类型
const obj = {}
obj[true] = 123
obj[123] = 123
obj[{a: 1}] = 123
console.log(Reflect.ownKeys(obj))
//[ '123', 'true', '[object Object]' ]
console.log(obj[{}])
// 123
我们发现真正对象的key
是字符串, 并且我们发现这么使用存在的其他问题。
那么Map
的出现解决了这些问题
const m = new Map()
const tom = {name: "tom"}
m.set(tom, 90)
// m.get()
// m.delete()
// m.clear()
// m.forEach()
Symbol 数据类型
独一无二的值
最主要的作用是给对象增加一个独一无二的属性
// const obj = {
// [Symbol("name")]: 123
// }
// console.log(obj)
// 场景 私有成员
const name = Symbol()
const person = {
[name]: 'zicoo',
say() {
console.log(this[name])
}
}
console.log(person.name)
person.say()
以上就是最简单的使用,那么Symbol
的其实使用场景呢
console.log(Symbol("foo") === Symbol("foo"))
console.log(Symbol.for("foo") === Symbol.for("foo"))
//false
//true
console.log(Symbol.for(true) === Symbol.for("true"))
// false 证明会将true 转成字符串
//Symbol 本身有好多方法的标识符
// eg:
//Symbol.hasInstance
//Symbol.iterator
const obj = {}
console.log(obj.toString())
//[object Object]
const objX = {
[Symbol.toStringTag] : "XObject"
}
console.log(objX.toString())
//[object XObject]
// 通过 for 无法不按理 objx中的 [Symbol.toStringTag], Object.keys 也不可以
console.log(Object.getOwnPropertySymbols(objX))
// [ Symbol(Symbol.toStringTag) ]
for ... of 循环
可遍历所有数据类型
基本用法
// 遍历数组
const arr = [100,200,300,400]
for (const item of arr) {
if (item > 100) {
break
}
console.log(item)
}
// 遍历map
const m = new Map()
m.set("foo", 123)
m.set("bar", 234)
for(const [key,value] of m) {
console.log(key, value)
}
当我们遍历数组时会出现什么情况
const obj = {name: "sam"}
for (const item of obj) {
console.log(item)
}
//obj is not iterable
问题来了报错了! 其实 数据结构的 Iterable
接口是使用 for ... of
的前提
迭代器
image.png我们发现无论
Array``Set``Map
的_proto_
原型上都有Symbol(Symbol.iterator)
这个方法。image.png
我们发现这个iterator
的作用相当于一个指针,每调用一次next()
,都会将value
指向下一个值,并且done
来区分是不是遍历完成
const set = new Set(["foo","bar","baz"])
const iterator = set[Symbol.iterator]()
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
//{ value: 'foo', done: false }
//{ value: 'bar', done: false }
//{ value: 'baz', done: false }
//{ value: undefined, done: true }
那我们简单实现一下可迭代接口Iterable
// 实现可迭代接口(Iterable)
const obj = {
store: ["foo", "bar", "baz"],
[Symbol.iterator]: function() { // Iterable
let index = 0
const self = this
return {
next: function() { // Iterator
return { //IterationResult
value: self.store[index],
done: index++ >= self.store.length
}
}
}
}
}
for (const item of obj) {
console.log(item)
}
生成器
function * foo() {
console.log("111")
yield 100
console.log("222")
yield 200
console.log("333")
yield 300
}
const result = foo()
console.log(result.next())
console.log(result.next())
console.log(result.next())
console.log(result.next())
// 使用generator 改造 iterator 方法
const obj = {
store: ["foo", "bar", "baz"],
[Symbol.iterator]: function * () { // Iterable
for(const item of obj.store) {
yield item
}
}
}
for (const item of obj) {
console.log(item)
}
它还有一个很重要的使用场景就是 异步编程, 可见之前的文章《异步编程(Promise、Generator、Async与Await)》
ES Modules
后续有详细介绍。略
ES2016
- Array.propotype.includes
- 指数运算 eg: Math.pow(2, 10) === 2 ** 10
ES2017
- Object.vaules: 返回所有值的数组
- Object.entries: 返回所有键值对数组
- Object.getOwnPropertyDescriptors: 获取所有属性 eg: get() set()
- String.prototype.padStart/String.prototype.padEnd: 填充字符串前面或后面
- Async/Await
网友评论