前言
这节标题超长了,难受箭头函数、set、map、proxy、symbol、reflect、generator
箭头函数
之前声明函数使用 function funName(arg1,...args){return args}
。
ES6 增加了新的声明函数方式 (arg1,...args)=>args
。
箭头函数的特点
- 匿名函数。通过上面例子可以看出,匿名函数没有名字。用法等价于之前的函数表达式。
- 简洁。
[1,2,3,4,0,-1,-2,-3].map(v=>Math.sign(v))
- 入参的省略。如果只有一个入参,那么括号可以省略。没有入参、多个入参、rest 参数(
...
)、解构({status, response}
)都需要加括号。
2.函数体的省略。如果没有花括号,那么只能传入一个表达式,表达式的结果会 return
。如果有花括号,就需要显式的return
,当然也可以不写默认 return
是 undefined
。
3.this 指向。
- 箭头函数的
this
对象指向定义时所在的对象,而不是使用时所在的对象。 - 箭头函数的
this
对象是不可变的。bind、call、apply、赋值等操作都无法改变。
4.不可以当做构造函数。因为上面的 this 问题。
5.没有 arguments
。
6.不能用作 Generator
函数。不可以使用 yield
命令
箭头函数的一些错误用法
- 不应该在声明对象的时候使用。因为这个时候里面的
this
指向定义时的环境。
obj = { name: 'app', jumps: () => { console.log(this.app); } }
Set、WeakSet
Set
特点是值唯一和有序。类似于数组,但是成员的值都是唯一的,没有重复的值,遍历的话是插入顺序。Array.from(new Set([1,2,3,1,1,2,3,1]))
依赖值唯一的特性实现去重。
Set 构造函数
new Set([iterable]);
使用可迭代的数组来创建。
new Set([1,2,3,4,2,1,1,1,12,3])
new Set('123123')
new Set(['123123',1,2,3])
new Set(document.querySelectorAll('a'))
5.
属性和方法
Set.prototype.size
:返回总数,等价于(Array.prototype.length
)
Set.prototype.add(value)
:添加,返回当前对象可链式操作。
Set.prototype.delete(value)
:删除,返回一个布尔值,表示删除是否成功。
Set.prototype.has(value)
:返回一个布尔值,表示该值是否为Set的成员。
Set.prototype.clear()
:清除所有成员,没有返回值。
Set.prototype.keys()
:返回键(key)的遍历器 Iterator。
Set.prototype.values()
:返回值(value)的遍历器 Iterator。
Set.prototype.entries()
:返回键值对([key, value])的遍历器 Iterator。
Set.prototype.forEach()
:使用回调函数(value, key, this)=>遍历每个成员
WeakSet
- 只能存放对象的集合
- 以弱引用方式保存。弱引用就是可以被垃圾回收(如果放入数组,那么你不知道什么实际是可以被回收的,因为数组是强引用),
- 没有总数
- 不可枚举。
Map、WeakMap
Map
其实 和 JavaScript 的对象(Object
)一样,本质上是键值对的集合(Hash 结构)。
但是传统上 Object
只能用字符串当作键,在使用上有很大的局限性。
Map
的数据结构也可以理解为对象,是键值对的集合。
但是 key
的范围不限于字符串,可以是其他类型(包括对象)都可以当作键。是一种更完善的 Hash
结构实现。
userinfo = {name: 'lilnong.top'} map = new Map() map.set(userinfo, {age: 123}) map.has(userinfo) map.get(userinfo)
构造函数
map = new Map([ ['key1', 'value1'], ['key2', 'value2'] ]); map.forEach( (key, value) => console.log(key, value) );
属性和方法
size
返回记录总数。(Object
可是没有这个属性的哟)
set(key, value)
赋值操作
get(key)
取值操作
has(key)
判断当前key
是否存在于集合中。
delete(key)
删除操作。返回true
,表明删除成功。返回false
表明删除失败,比如你删除了一个不存在的key
。
clear()
清空所有。
- 遍历方法。(插入顺序)
Map.prototype.keys()
:返回键名的遍历器。Map.prototype.values()
:返回键值的遍历器。Map.prototype.entries()
:返回所有成员的遍历器。Map.prototype.forEach()
:遍历 Map 的所有成员。
weakMap
WeakMap
只接受对象作为键名(null
除外),不接受其他类型的值作为键名。
WeakMap
的键名所指向的对象,不计入垃圾回收机制。
因为是弱引用,所以没有遍历方法、没有 size,不支持 clear()。
proxy
Proxy
用于修改操作(getset)的默认行为。
说到这里是不是想到了 Object.defineProperty
?其实也和 JavaBean
那种操作 getter、setter
差不多。
等同于在语言层面做出修改,对编程语言进行编程,属于一种“元编程”(meta programming )。
Proxy 可以理解成,在目标对象之前架设个代理,对该对象的访问,都必须先通过代理,这样可以对外界的访问进行过滤和改写。
vue 2.x 基于 Object.defineProperty
来实现的,但是会有一些场景监测不到(array.lenth、$set)。
vue 3.x 使用了 Proxy
来做了,可以监测到更多的场景。当然,基于兼容性考虑还是可以回退到 Object.defineProperty
。
var obj = new Proxy({}, { get: function (target, propKey, receiver) { console.log(`getting ${propKey}!`); return Reflect.get(target, propKey, receiver); }, set: function (target, propKey, value, receiver) { console.log(`setting ${propKey}!`); return Reflect.set(target, propKey, value, receiver); } });
从下面这个例子我们可以看到,我们可以只设置读的代理,不管读取什么都返回 www.lilnong.top
。然后我们没有设置写的代理,值已经被真正的写入了。
构造函数
var proxy = new Proxy(target, handler);
支持代理的操作
key | arguments | 触发时机 | demo |
get |
(target, propKey, receiver) |
读取属性时触发 | proxy.title 和 proxy['title'] |
set |
(target, propKey, value, receiver) |
设置属性时触发 | proxy.title = 'lilnong.top' 或 proxy['title'] = 'lilnong.top' |
has |
(target, propKey) |
in 时触发 |
title in proxy |
deleteProperty |
(target, propKey) |
delete 时触发 |
delete proxy.title |
ownKeys |
(target) |
获取 key 合集时触发 | Object.getOwnPropertyNames(proxy) 、Object.getOwnPropertySymbols(proxy) 、Object.keys(proxy) 、for...in |
getOwnPropertyDescriptor |
(target, propKey) |
读取属性时触发 | Object.getOwnPropertyDescriptor(proxy, propKey) |
defineProperty |
(target, propKey, propDesc) |
读取属性时触发 | Object.defineProperty(proxy, propKey, propDesc) 、Object.defineProperties(proxy, propDescs) |
preventExtensions |
(target) |
读取属性时触发 | Object.preventExtensions(proxy) |
getPrototypeOf |
(target) |
读取属性时触发 | Object.getPrototypeOf(proxy) |
isExtensible |
(target) |
读取属性时触发 | Object.isExtensible(proxy) |
setPrototypeOf |
(target, proto) |
读取属性时触发 | Object.setPrototypeOf(proxy, proto) |
apply |
(target, object, args) |
proxy 为函数被调用时触发 | proxy(...args) 、proxy.call(object, ...args) 、proxy.apply(...) |
construct |
(target, object, args) |
proxy 为实例被New 时触发 | new proxy(...args)) |
注意事项
- 严格模式下,
set
代理执行完毕应该返回true
,否则会报错。
symbol
ES5 的对象属性名都是字符串
,这容易造成属性名的冲突。比如,你使用了一个他人提供的对象,但又想为这个对象添加新的方法(mixin 模式),新方法的名字就有可能与现有方法产生冲突。如果有一种
机制,保证每个属性的名字都是独一无二的就好了,这样就从根本上防止属性名的冲突。这就是 ES6 引入
Symbol
的原因。
ES6 引入了一种新的原始数据类型
Symbol
,表示独一无二的值。它是 JavaScript 语言的第七种数据类型,前六种是:undefined
、null
、布尔值(Boolean
)、字符串(String
)、数值(Number
)、对象(Object
)。
Symbol 值通过
Symbol
函数生成。这就是说,对象的属性名现在可以有两种类型,一种是原来就有的字符串,另一种就是新增的 Symbol 类型。凡是属性名属于 Symbol 类型,就都是独一无二的,可以保证不会与其他属性名产生冲突。
s = Symbol(); typeof s // "symbol" s1 = Symbol('foo'); s1 // Symbol(foo) s1.toString() // "Symbol(foo)" s2 = Symbol('bar'); s2 // Symbol(bar) s2.toString() // "Symbol(bar)"
reflect
Reflect
对象与Proxy
对象一样,也是 ES6 为了操作对象而提供的新 API。
Reflect
对象的设计目的有这样几个。
(1) 将
Object
对象的一些明显属于语言内部的方法(比如Object.defineProperty
),放到Reflect
对象上。现阶段,某些方法同时在Object
和Reflect
对象上部署,未来的新方法将只部署在Reflect
对象上。也就是说,从Reflect
对象上可以拿到语言内部的方法。
(2) 修改某些
Object
方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)
在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)
则会返回false
。....
....https://es6.ruanyifeng.com/#d...
generator
Generator
函数也是 ES6 提供的一种异步编程解决方案。现在一般都是 Promise
或者 await/async
,就不展开说了。
Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍 Generator 函数的语法和 API,它的异步编程应用请看《Generator 函数的异步应用》一章。
Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,
function
关键字与函数名之间有一个星号;二是,函数体内部使用yield
表达式,定义不同的内部状态(yield
在英语里的意思就是“产出”)。....