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 方法返回一个包含 valuedone 两个属性的对象;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 属性本身是一个函数,就是当前数据结构默认的遍历器生成函数;执行这个函数,就会返回一个遍历器对象( valuedone

  • 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)

posted @ 2018-03-27 08:57  执着的程序员~  阅读(1178)  评论(0编辑  收藏  举报