Iterator 遍历器 与 Proxy 代理
一、Iterator 遍历器
1. Iterator 概述
-
Iterator 产生场景: JS 表示 “集合” 的数据结构 主要由 Array、Object;ES6 又增加了 Map、Set;为提供一种统一的访问机制,变产生了 遍历器(Interator)
-
遍历器(Iterator)是一种机制、也是一种接口;为各种不同的数据结构提供统一的访问机制
for...of
;任何数据结构只要部署 Iterator 接口,就可以完成遍历操作
2. Iterator 的作用
-
为各种数据结构,提供一个统一的、简便的访问机制:
for...of
循环 -
使得数据结构的成员能够按某种次序排列
3. Iterator 工作原理
- 原理:
-
遍历器提供了一个指针,指向当前对象的某个属性,使用next方法,就可以将指针移动到下一个属性
-
next
方法返回一个包含value
和done
两个属性的对象;value
属性是当前遍历位置的值、done
属性是一个布尔值(表示遍历是否结束)
-
//模拟遍历器原理
function makeIterator(array){
var nextIndex = 0;
return {
next: function(){
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
}
}
var it = makeIterator(['a', 'b']);
console.log(it.next());//{ value: 'a', done: false }
console.log(it.next());//{ value: 'b', done: false }
console.log(it.next());//{ value: undefined, done: true }
4. 默认的 Iterator 接口
-
ES6 规定,默认的 Iterator 接口部署在数据结构的
Symbol.iterator
属性上;Symbol.iterator
属性本身是一个函数,就是当前数据结构默认的遍历器生成函数;执行这个函数,就会返回一个遍历器对象(value
和done
) -
ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被
for...of
循环遍历- Array
- Map
- Set
- String
- TypedArray
- 函数的 arguments 对象
- NodeList 对象
-
对象 不具备
Symbol.iterator
属性,所以不能被for...of
循环遍历; 手动添加遍历器后,就可以遍历了 (但实际开发中 很少手动添加 遍历器)
二、遍历数组
-
for 循环 --- ES5 遍历数组(break 可终止循环)
-
forEach( ) --- ES5 遍历数组(return 可终止本次循环)
-
for-of 循环 --- ES6 推荐遍历数组
- 数组原生具备iterator接口(即默认部署了Symbol.iterator属性),for...of循环本质上就是调用这个接口产生的遍历器
-
for-in 循环 --- ES5 不推荐遍历数组;遍历对象的一种方式
-
for-in
循环 是专门为循环对象设计的, 只能 获取到键值;不能获取到键名 -
缺陷: 遍历数组时,获取的键值也就是数组的索引,但索引类型是字符串类型
-
三、遍历对象 ---> for-of
- for-of 遍历对象 --- 【最佳实践】
// 最佳实践
const obj = {
name: 'zhangxin',
age: 18
};
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
- for-in 循环 --- ES5 遍历对象
- 只能 获取到键值;不能获取到键名
四、对象 / 函数 的 操作拦截 ---> Proxy(JS 代理)
-
可理解为: Proxy 是对象操作时的一层拦截处理,外界对 JS 对象的访问,都需要通过这层拦截;因此,通过proxy 机制,可以对外界的访问进行过滤和改写
-
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
-
语法:
new Proxy(target, handler)
-
参数
target
:要拦截的目标对象 -
参数
handle
(对象类型):多个函数,定义拦截行为
-
-
示例:
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.name // 35
proxy.title // 35
- 注意:
-
要想
proxy
拦截起作用,必须针对Proxy实例(上例是proxy对象)进行操作, 而不是针对目标对象(上例是空对象)进行操作 -
如果handler没有设置任何拦截,那就等同于直接通向原对象
-
Proxy 实例也可以作为其他对象的原型对象
-
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
let obj = Object.create(proxy);
obj.time // 35
// 如上:proxy对象是obj对象的原型,obj对象本身并没有time属性,所以根据原型链,会在proxy对象上读取该属性,导致被拦截
五、Proxy 代理示例的 拦截方法
1. get( ) ---> 拦截 属性 读取 操作
-
语法:
get: function(target, property, proxy) { }
-
参数
target
:目标对象(必填) -
参数
property
:要读取的属性名(必填) -
参数
proxy
:this关键字指向的那个对象(可选)
-
-
get方法可以继承
-
示例:
// 如果读取obj对象继承的属性时,拦截会生效
let proto = new Proxy({}, {
get(target, propertyKey, receiver) {
console.log('GET ' + propertyKey);
return target[propertyKey];
}
});
let obj = Object.create(proto);
obj.foo // "GET foo"
2. set( ) ---> 拦截 属性 赋值 操作
- 语法:
get: function(target, property, value, proxy) { }
-
参数
target
:目标对象(必填) -
参数
property
:要添加的属性名(必填) -
参数
value
:要添加的属性的属性值(必填) -
参数
proxy
:this关键字指向的那个对象(可选)
-
3. apply( ) ---> 拦截 函数的调用、call、apply 操作
-
语法:
apply (target, ctx, args) { }
-
参数
target
:目标对象(可选) -
参数
ctx
:目标对象的上下文对象(this)(可选) -
参数
args
:目标对象的参数数组(可选)
-
-
示例:
var target = function () { return 'I am the target'; };
var handler = {
apply: function () {
return 'I am the proxy';
}
};
var p = new Proxy(target, handler);
console.log(p()); // "I am the proxy"
4. 其他拦截方法
-
has(target, propKey)
:拦截propKey in proxy
的操作,返回一个布尔值 -
deleteProperty(target, propKey)
:拦截delete proxy[propKey]
的操作,返回一个布尔值 -
ownKeys(target)
:拦截Object.getOwnPropertyNames(proxy)
、Object.keys(proxy)
、Object.getOwnPropertySymbols(proxy)
,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而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)