个人笔记---阮一峰ECMAScript 6 入门

225 阅读7分钟

Proxy


  1. 概述:在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,可以对外界的访问进行过滤和改写。
    • 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)。
  2. Proxy.revocable() 方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。
  3. this

Reflect

  1. 概述
    • 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。
    • 修改某些Object方法的返回结果,让其变得更合理。比如,Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出一个错误,而Reflect.defineProperty(obj, name, desc)则会返回false。
    • 让Object操作都变成函数行为。
    • Reflect对象的方法与Proxy对象的方法一一对应
  2. 静态方法
    • 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接口


  1. Iterator接口是为for...of遍历而服务的,一种数据结构只要部署了Iterator接口,就是可遍历的。
  2. 一般有以下几种数据结构是默认可遍历的:Array,Map,Set,String,函数的 arguments 对象,NodeList 对象,TypedArray
  3. 默认的Iterator部署在数据结构的Symbol.iterator属性
  4. 所以想让对象也编程可遍历的,可以在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)
  1. 调用iterator接口的场合:解构赋值,扩展运算符,yield*,其他
  2. 遍历器对象还可以有return,throw方法
  3. 只要部署了Symbol.iterator属性,就是有iterator接口,就可以用for...of循环,for...of循环的有点:
    • forEach循环不能中断,但是for...of可以;
    • 没有for...in那些缺点。
    • 提供了遍历所有数据结构的统一操作接口
  4. for...in循环主要是为遍历对象而设计的,for...in循环的缺点:
    • 以字符串作为键名;
    • 遍历循环可枚举(enumerable)对象,可用defineProperty()定义单个属性或Object.defineProperties()定义多个属性;
    • 某些情况下,for...in循环会以任意顺序遍历键名;

Generator 函数的语法


  1. 概念:
    • 执行Generator函数会返回一个遍历器对象,函数的形式时function与函数名之间加*。
    • 函数内部使用yield表达式定义不同的内部状态。
    • 调用Generator函数返回的是指向内部状态的指针对象。
    • Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
    • Generator中不用yield会变成暂缓执行函数
    • Generator 函数调用时会返回遍历器对象,该对象的Symbol.iterator属性执行后返回本身
  2. next方法
    • yield表达式返回underfined,next方法可以传递参数,作为上一个yield表达式的返回值
    • gen.next(1);
  3. 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)
      }
      
  4. 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('出错了'));
  5. Generator.prototype.return()
    • 返回给定的值,并且终结遍历 Generator 函数。返回值的value属性就是return方法的参数,return若没有参数返回value为underfined。
    • 如果 Generator 函数内部有try...finally代码块,且正在执行try代码块,那么return方法会导致立刻进入finally代码块,执行完以后,整个函数才会结束。返回return()方法指定的返回值。
    • gen.return(2);
  6. 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)])
      
  7. 作为对象属性的 Generator 函数
let obj = {
  * myGeneratorMethod() {
    ···
  }
};
//或者
let obj = {
  myGeneratorMethod: function* () {
    // ···
  }
};
  1. 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();
      
  2. Generator实现实现状态机
var clock = function* () {
  while (true) {
    console.log('Tick!');
    yield;
    console.log('Tock!');
    yield;
  }
};
  1. 应用
    • 异步操作的同步化表达
    • 控制流管理
    • 部署 Iterator 接口
    • 作为数据结构