欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ES6看完必会第十二章------ Proxy和Reflect (可私信解惑,不会来捶我)

程序员文章站 2022-07-16 22:01:16
...

Proxy 概述

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。

Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写

 

下面是常见的 Proxy 支持的拦截操作方法。

(1)get(target, propKey, receiver)

    拦截对象属性的读取,比如proxy.foo和proxy['foo']。

    最后一个参数receiver是一个对象,可选,参见下面Reflect.get的部分。

get方法用于拦截某个属性的读取操作。上文已经有一个例子,下面是另一个拦截读取操作的例子。

var person = {
  name: "jack"
};
var proxy = new Proxy(person, {
  get: function(target, property) {
    if (property in target) {       
      return target[property];
    } else {       
      throw new ReferenceError("Property \"" + property + "\" does not exist.");
    }
  }
});
proxy.name     // "jack"
proxy.age     // 抛出一个错误

上面代码表示,如果访问目标对象不存在的属性,会抛出一个错误。如果没有这个拦截函数,访问不存在的属性,只会返回undefined。

(2)set(target, propKey, value, receiver)

    拦截对象属性的设置,比如proxy.foo = v或proxy['foo'] = v,返回一个布尔值。

set方法用来拦截某个属性的赋值操作。

let obj = {
  set: function(obj, prop, value) {
    if (prop === 'age') {
      if (!Number.isInteger(value)) {
        throw new TypeError('错误信息:不是整数');
      }
      if (value > 200) {
        throw new RangeError('错误信息:年龄大于200');
      }
    }
    // 对于age以外的属性,直接保存
    obj[prop] = value;
  }
};
let person = new Proxy({}, obj);
person.age = 13;
person.age     // 13
person.age = 'jack'     // age不是整数报错
person.age = 300     // age大于200报错

上面代码中,由于设置了存值函数set,任何不符合要求的age属性赋值都会抛出一个错误。

 

(3)has(target, propKey)

    拦截propKey in proxy的操作,以及对象的hasOwnProperty方法,返回一个布尔值。

has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是in运算符。

var handler = {
  has (target, key) {
    if (key[0] === '_') {
      return false;
    }
    return key in target;
  }
};
var target = { _prop: 'foo', prop: 'foo' };
var proxy = new Proxy(target, handler);
console.log(  '_prop' in proxy  )        // false

上面代码中,如果原对象的属性名的第一个字符是下划线,proxy.has就会返回false,从而不会被in运算符发现。

(4)deleteProperty(target, propKey)

    拦截delete proxy[propKey]的操作,返回一个布尔值。

deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除。

var handler = {
  deleteProperty (target, key) {
    invariant(key, 'delete');
    return true;
  }
};
function invariant (key, action) {
  if (key[0] === '_') {
    throw new Error('删除下划线开头的报错');
  }
}
var target = { _prop: 'foo' };
var proxy = new Proxy(target, handler);
delete proxy._prop	    	// 报错内容:删除下划线开头的报错

上面代码中,deleteProperty方法拦截了delete操作符,删除第一个字符为下划线的属性会报错。

(5)apply(target, object, args)

    拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)。

apply方法拦截函数的调用、call和apply操作。

apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。

var twice = {
  apply (target, ctx, args) {
    return Reflect.apply(...arguments) * 2;
  }
};

function sum (num) {
  return num + 1 ;
};

var proxy = new Proxy(sum, twice);        // 第一个参数相当于原值,第二个参数相当于过滤器,变量proxy相当于过滤后的值

proxy(2)     // 6
proxy.call(null, 5, 6)     // 22
proxy.apply(null, [7, 8])     // 30

上面代码中,每当执行proxy函数(直接调用或call和apply调用),就会被apply方法拦截。直接调用Reflect.apply方法也会被拦截。

(6)construct(target, args)

    拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)。

construct方法用于拦截new命令。

construct方法可以接受两个参数。

  • target: 目标对象
  • args:构建函数的参数对象
var p = new Proxy(function() {}, {
  construct: function(target, args) {
    console.log('called: ' + args.join(', '));
    return { value: args[0] * 10 };
  }
});
new p(1).value
// "called: 1"
// 10

construct方法返回的必须是一个对象,否则会报错。

Reflect概述

 

ES6 中将 Object 的一些明显属于语言内部的方法移植到了 Reflect 对象上(当前某些方法会同时存在于 Object 和 Reflect 对象上),未来的新方法会只部署在 Reflect 对象上。

Reflect 对象对某些方法的返回结果进行了修改,使其更合理。

Reflect 对象使用函数的方式实现了 Object 的命令式操作。

每一个Proxy对象的拦截操作(get、delete、has),内部都调用对应的Reflect方法,保证原生行为能够正常执行。

Reflect方法

Reflect.get(target, name, rec)     

查找并返回 target 对象的 name 属性。可以有三个参数,target必须为对象类型,name是target的属性,rec为另一个对象。

let obj = {
    name: "jack",
    age: 14,
    get userInfo(){
        return this.name + this.age;
    }
}

Reflect.get(obj, 'name');     // "jack"

// 当 target 对象中存在getter 方法, getter 方法的 this 会指向  rec参数的name属性
let receiver = {
    name: "haha",
    age: 13
}

Reflect.get(obj, 'userInfo', receiver);     // haha13

//  target 对象不存在该属性,返回 undefined
Reflect.get(obj, 'aaa');     // undefined

// 当 target 不是对象时,会报错
Reflect.get(1, 'name');     //  报错

Reflect.set(target, name, value, receiver)

将 target 的 name 属性设置为 value。返回值为true 表示修改成功,false 表示失败。当 target 为不存在的对象时即报错。

let obj= {
    name: "jack",
    age: 24,
    set userInfo(value){
        return this.name = value;
    }
}

obj.name; 	    // jack
Reflect.set(obj, 'name', "haha"); 
obj.name;     // haha

// value值为空,表示删除属性
Reflect.set(obj, 'name', ); 
obj.name; 	     // undefined

// 当 target对象有set方法,setter 方法中的指向receiver, 所以修改的实际上是 receiver 的属性
let receiver = {
    name: 8888888888
}

Reflect.set(obj, 'userInfo', "这是修改后的值", receiver);
receiver.name; 	    // 这是修改后的值

第十三章: ES6看完必会第十三章------ Iterator(遍历器)(可私信解惑,不会来捶我)

相关标签: ES6快速入门 ES6