Proxy
- 概述:在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,可以对外界的访问进行过滤和改写。
- Proxy 构造函数:target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
var proxy = new Proxy(target, handler); - 将 Proxy 对象,设置到object.proxy属性,从而可以在object对象上调用。
var object = { proxy: new Proxy(target, handler) }; - 作为其他对象的原型对象
var proxy = new Proxy({}, { get: function(target, propKey) { return 35; } }); let obj = Object.create(proxy);//Object.create可以设置原型对象 obj.time // 35 - Proxy 支持的拦截操作
- get(target, propKey, receiver):拦截对象属性的读取
- set(target, propKey, value, receiver):拦截对象属性的设置
- has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
- deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
- ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
- 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, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。
- Proxy 构造函数:target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。
- Proxy.revocable() 方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。
- this
Reflect
- 概述
- 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。
- 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
- 让Object操作都变成函数行为。
- Reflect对象的方法与Proxy对象的方法一一对应
- 静态方法
- Reflect.apply(target, thisArg, args)
- Reflect.construct(target, args)
- Reflect.get(target, name, receiver)
- Reflect.set(target, name, value, receiver)
- Reflect.defineProperty(target, name, desc)
- Reflect.deleteProperty(target, name)
- Reflect.has(target, name)
- Reflect.ownKeys(target)
- Reflect.isExtensible(target)
- Reflect.preventExtensions(target)
- Reflect.getOwnPropertyDescriptor(target, name)
- Reflect.getPrototypeOf(target)
- Reflect.setPrototypeOf(target, prototype)
Iterator接口
- Iterator接口是为for...of遍历而服务的,一种数据结构只要部署了Iterator接口,就是可遍历的。
- 一般有以下几种数据结构是默认可遍历的:Array,Map,Set,String,函数的 arguments 对象,NodeList 对象,TypedArray
- 默认的Iterator部署在数据结构的Symbol.iterator属性
- 所以想让对象也编程可遍历的,可以在Object的原型链上加上方法Symbol.iterator
Object.prototype[Symbol.iterator] = function () {
let nextIndex = 0
return {
next:()=> {
if(nextIndex < this.length)
return { value: this[nextIndex++],done:false }
else return {value: 'underfined',done:true}
}
}
}
这段代码利用的闭包,nextIndex是一个私有变量,next是一个特权方法,可以通过next方法来改变内部的指针方向,控制他指向对象的第几个参数,如上的Iterator接口针对类数组,也通过这个代码可以了解到为什么类数组能够转成数组。
当然也可以使用原型链的方法定义可遍历接口,或者用bind(),call()
Object.prototype[Symbol.iterator]=[][Symbol.iterator]
或者
obj[Symbol.iterator]=Array.prototype[Symbol.iterator].bind(obj)
- 调用iterator接口的场合:解构赋值,扩展运算符,yield*,其他
- 遍历器对象还可以有return,throw方法
- 只要部署了Symbol.iterator属性,就是有iterator接口,就可以用for...of循环,for...of循环的有点:
- forEach循环不能中断,但是for...of可以;
- 没有for...in那些缺点。
- 提供了遍历所有数据结构的统一操作接口
- for...in循环主要是为遍历对象而设计的,for...in循环的缺点:
- 以字符串作为键名;
- 遍历循环可枚举(enumerable)对象,可用defineProperty()定义单个属性或Object.defineProperties()定义多个属性;
- 某些情况下,for...in循环会以任意顺序遍历键名;
Generator 函数的语法
- 概念:
- 执行Generator函数会返回一个遍历器对象,函数的形式时function与函数名之间加*。
- 函数内部使用yield表达式定义不同的内部状态。
- 调用Generator函数返回的是指向内部状态的指针对象。
- Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
- Generator中不用yield会变成暂缓执行函数
- Generator 函数调用时会返回遍历器对象,该对象的Symbol.iterator属性执行后返回本身
- next方法
- yield表达式返回underfined,next方法可以传递参数,作为上一个yield表达式的返回值
- gen.next(1);
- for...of
- 一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象
- for...of循环可以自动遍历 Generator 函数运行时生成的Iterator对象,不需要调用next方法
- 斐波那契数列
function* fibonacci(){ let [prev,curr]=[0,1] for(;;){ yield curr; [prev,curr]=[curr,prev+curr] } } for(n of fibonacci()){ if (n>1000) break ; console.log(n) }
- Generator.prototype.throw()
- throw方法,可以在函数体外抛出错误,然后在 Generator 函数体内通过try...catch捕获。
- throw方法抛出的错误要被内部捕获,前提是必须至少执行过一次next方法。
- throw方法被捕获以后,会附带执行下一条yield表达式。也就是说,会附带执行一次next方法。
- 只要 Generator 函数内部部署了try...catch代码块,那么遍历器的throw方法抛出的错误,不影响下一次遍历。
- 一旦 Generator 执行过程中抛出错误,且没有被内部捕获,就不会再执行下去了。如果此后还调用next方法,将返回一个value属性等于undefined、done属性等于true的对象,即 JavaScript 引擎认为这个 Generator 已经运行结束了。
- gen.throw(new Error('出错了'));
- Generator.prototype.return()
- 返回给定的值,并且终结遍历 Generator 函数。返回值的value属性就是return方法的参数,return若没有参数返回value为underfined。
- 如果 Generator 函数内部有try...finally代码块,且正在执行try代码块,那么return方法会导致立刻进入finally代码块,执行完以后,整个函数才会结束。返回return()方法指定的返回值。
- gen.return(2);
- yield* 表达式
- 在一个 Generator 函数里面执行另一个 Generator 函数。
- 如果没有yield*,需要在前者的函数体内部,自己手动完成遍历。
- 有return语句时,需要用var value = yield* iterator的形式获取return语句的值.
- 扁平化数组
function* isTree(tree){ if(Array.isArray(tree)){ for (const item of tree) { yield* isTree(item) } }else{ yield tree; } } const tree = [ 'a', ['b', ['c','a']], ['d', 'e'] ]; console.log([...isTree(tree)]) - 使用yield*语句遍历完全二叉树
function Tree(left,label,right){ this.left=left this.label=label this.right=right } function* inorder(t){ if(t){ yield* inorder(t.left) yield t.label yield* inorder(t.right) } } function make(array){ if(array.length===1) return new Tree(null,array[0],null) return new Tree(make(array[0]),array[1],make(array[2])) } let tree = make([[['a'], 'b', ['c']], 'd', [['e'], 'f', ['g']]]); console.log([...inorder(tree)])
- 作为对象属性的 Generator 函数
let obj = {
* myGeneratorMethod() {
···
}
};
//或者
let obj = {
myGeneratorMethod: function* () {
// ···
}
};
- Generator 函数的this
- Generator 函数总是返回一个遍历器,ES6 规定这个遍历器是 Generator 函数的实例,也继承了 Generator 函数的prototype对象上的方法。但是不能作为普通的构造函数,因为返回的不是this对象。
- 返回正常对象实例的办法:F.call(F.prototype)放在构造函数中
function* gen() { this.a = 1; yield this.b = 2; yield this.c = 3; } function F() { return gen.call(gen.prototype); } var f = new F();
- Generator实现实现状态机
var clock = function* () {
while (true) {
console.log('Tick!');
yield;
console.log('Tock!');
yield;
}
};
- 应用
- 异步操作的同步化表达
- 控制流管理
- 部署 Iterator 接口
- 作为数据结构