zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    58.与人相处的艺术
    26.随时随俗
    24.心平气和
    61.扶树与扶人
    47.非要坚持下去吗
    42.有“舍”才有“得”
    62.离阳光只有五十米
    49.用微笑把痛苦埋葬
    60.换个角度,你便是赢家
    35.忍是大智、大勇、大福
  • 原文地址:https://www.cnblogs.com/zxvictory/p/8655418.html
Copyright © 2011-2022 走看看