Reflect和Proxy

一.Reflect概述

Reflect和Proxy都是es6操作对象的新API,Reflect的目的有:  

1)将Object对象上的语言内部方法,加到Reflect对象上,比如Object.defineProperty。 

        现阶段,这些方法同时在Object和Reflect上,未来的新方法只在Reflect对象上。

        意义:Reflect对象上能拿到语言内部的方法 

2) 修改某些Object方法的返回结果,使其更为合理。比如Object.defineProperty(obj, name, desc)在无法定义属性时,会抛出错误,而Reflect.defineProperty(obj, name, desc)会返回false。

// es5写法 可以看到结果抛出错误了
  obj={
    name: '不可扩展噢'
  }
  Object.preventExtensions(obj)
  try {
    Object.defineProperty(obj, 'age', {
      value: 18
    })
  } catch {
    console.log('抛出错误')
  }
  
// es6写法 不会抛出错误,而是返回结果false
  try {
    let result = Reflect.defineProperty(obj, 'age', {
      value: 20
    })
    console.log(result) // false
  } catch {
    console.log('不会抛出异常')
  }

3)Object的操作变为函数行为,某些操作是命令式的,比如name in obj delete obj[name],现在Reflect的函数行为则是Reflect.has(obj, name),Reflect.deleteProperty(obj, name)

//Object命令写法
  console.log('name' in obj) //true

//Reflect函数写法
  console.log(Reflect.has(obj, 'name')) //true

4)Reflect上的方法和Proxy一一对应,只要是Proxy对象上的方法,都能在Reflect上找到相应的方法。Proxy能方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是不管Proxy怎么修改默认行为,都能通过Reflect获取默认行为

let info = new Proxy({
    name: '牛牛'
  }, {
    set: (target, key, value, receiver)=> {
      // 默认行为
      let result = Reflect.set(target, key, value, receiver)
      if(result) {
        // 修改添加的行为
        console.log(`proxy ${key} on ${target} set to ${value}`)
      }
      return result
    }
  })
  console.log(info) // Proxy {name: '牛牛'}
  info.name = '改名了' // 输出了日志:proxy name on [object Object] set to 改名了
  console.log(info) // Proxy {name: '改名了'}、

//该例Proxy拦截了目标对象的属性赋值行为,用Reflect.set将值赋值给对象的
属性,确保完成默认行为,在此基础上添加其他行为

 下一例子:Proxy对象种的每一个拦截操作(getdeletehas),内部都调用对应的Reflect方法,保证默认行为正常执行。添加的行为,就是每一个操作都输出一行日志。

let config = new Proxy(
    {
      name: '红星',
      age: 16
    },
    {
      get: (target, key, receiver)=> {
        console.log('get', target, key)
        return Reflect.get(target, key ,receiver)
      },
      deleteProperty: (target, key)=> {
        console.log('delete', key)
        return Reflect.deleteProperty(target, key)
      },
      has: (target, key)=> {
        console.log('has', key)
        return Reflect.has(target, key)
      }
    }
  )
  if('name' in config) { // 输出日志:has name
    console.log(config.name) 
    // get {name: '红星', age: 16} name 
    // 红星
    delete config['age'] // delete age
  }

同时Reflect写法让一些操作可读性更高:

//原写法
Function.prototype.apply.call(Math.floor, undefined, [1.87]) //1
// Fuction.prototype.apply(undefined, [1.87])
// Function.prototype.apply指向Math.floor

//现写法
Reflect.apply(Math.floor, undefined, [1.87]) //1

二. 静态方法

Reflect一共有13个静态方法:


一部分是Object的方法添加到Reflect对象上,有些做了小改动,使其更合理:

1.Reflect.defineProperty(target, name, desc) == Object.defineProperty(target, name, desc),用于为对象定义属性。

1)无法定义属性时,前者不会报错,返回false(成功返回true)

2)第一个参数不是对象时,两者都抛出错误

Object.defineProperty(1, 'foo')
// TypeError: Object.defineProperty called on non-object
Reflect.defineProperty(1, 'foo')
// TypeError: Reflect.defineProperty called on non-object

3)可以和proxy配合使用。Proxy.defineProperty对属性赋值设置了拦截,然后使用Reflect.defineProperty完成了赋值

const p = new Proxy({}, {
  defineProperty(target, prop, descriptor) {
    console.log(descriptor);
    return Reflect.defineProperty(target, prop, descriptor);
  }
});

p.foo = 'bar';
// {value: "bar", writable: true, enumerable: true, configurable: true}

p.foo // "bar"

2.Reflect.getPrototypeOf(target) == Object.getPrototypeOf(target),返回指定对象的原型,区别在于传参不是对象时,Object.getPrototypeOf先将参数转为对象再运行,而Reflect.getPrototypeOf会报错

const myObj = new FancyThing();
// 旧写法
Object.getPrototypeOf(myObj) === FancyThing.prototype; //true
// 新写法
Reflect.getPrototypeOf(myObj) === FancyThing.prototype; //true
// 区别
Object.getPrototypeOf(1) // Number 先转为{[[PrimitiveValue]]: 0}对象再执行
Reflect.getPrototypeOf(1) // 报错

3.Reflect.setPrototypeOf(target, prototype) == Object.setPrototypeOf(target, prototype),将指定对象的原型设为新对象。

1)前者返回布尔值。如目标对象无法扩展,前者返回false,后者则报错

const myObj = {a: 1};
// 旧写法
Object.setPrototypeOf(myObj, Array.prototype); //返回myObj对象
// 新写法
Reflect.setPrototypeOf(myObj, Array.prototype); //返回true

Object.setPrototypeOf(Object.freeze({}), null); // 报错
Reflect.setPrototypeOf(Object.freeze({}), null); // 返回false

2)当第一个参数不是对象时,前者会报错,而后者返回第一个参数自身

Reflect.setPrototypeOf(1, {})
// TypeError: Reflect.setPrototypeOf called on non-object

Object.setPrototypeOf(1, {}) // 1

3)当第一个参数是nullundefined时,两者都报错

Reflect.setPrototypeOf(null, {})
// TypeError: Reflect.setPrototypeOf called on non-object

Object.setPrototypeOf(null, {})
// TypeError: Object.setPrototypeOf called on null or undefined

4.Reflect.getOwnPropertyDescriptor(target, name) == Object.getOwnPropertyDescriptor(target, name),指定对象存在该属性时,返回该属性的描述符

var myObject = {};
Object.defineProperty(myObject, 'hidden', {
  value: true,
  enumerable: false,
});

// 旧写法 
var oldDesc = Object.getOwnPropertyDescriptor(myObject, 'hidden');

// 新写法
var newDesc = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');
console.log(oldDesc, newDesc)
// 打印都是 {value: true, writable: false, enumerable: false, configurable: false}

 1)当一个参数不是对象,前者会报错,后者则返回的undefined

Reflect.getOwnPropertyDescriptor(1, 'foo') 
// TypeError: Reflect.getOwnPropertyDescriptor called on non-object
Object.getOwnPropertyDescriptor(1, 'foo') //返回undefined

5.Reflect.isExtensible(target) == Object.isExtensible(target),都是返回布尔值,表示目标对象是否可以扩展

const myObject = {};

// 旧写法
Object.isExtensible(myObject) // true

// 新写法
Reflect.isExtensible(myObject) // true

1)当第一个参数不是对象,前者报错,后者返回false

Reflect.isExtensible(1) // TypeError: Reflect.isExtensible called on non-object
Object.isExtensible(1) // false

6.Reflect.preventExtensions(target) == Object.preventExtensions(target),用于让一个对象变为不可扩展。前者返回布尔值,表示是否操作成功,后者返回的是对象

var myObject = {};

// 旧写法
Object.preventExtensions(myObject) // Object {}

// 新写法
Reflect.preventExtensions(myObject) // true

1)如果参数不是对象,Object.preventExtensions在 ES5 环境报错,在 ES6 环境返回传入的参数,而Reflect.preventExtensions会报错。

// ES5 环境
Object.preventExtensions(1) // 报错

// ES6 环境
Object.preventExtensions(1) // 1

// 新写法
Reflect.preventExtensions(1) // 报错

7.Reflect.apply(target, thisArg, args) == Function.prototype.apply.call(target, thisArg, args),用于绑定this对象后执行函数。 如果要绑定一个函数的this对象,可以这样写fn.apply(obj, args),但是如果函数定义了自己的apply方法,就只能写成Function.prototype.apply.call(fn, obj, args),采用Reflect对象可以简化这种操作。

const ages = [11, 33, 12, 54, 18, 96];

// 旧写法
const youngest = Math.min.apply(Math, ages);
const oldest = Math.max.apply(Math, ages);
const type = Object.prototype.toString.call(youngest);

// 新写法
const youngest1 = Reflect.apply(Math.min, Math, ages);
const oldest1 = Reflect.apply(Math.max, Math, ages);
const type1 = Reflect.apply(Object.prototype.toString, youngest, []);

8.Reflect.ownKeys(target),用于返回对象的所有属性,基本等同于Object.getOwnPropertyNamesObject.getOwnPropertySymbols之和

var myObject = {
  foo: 1,
  bar: 2,
  [Symbol.for('baz')]: 3,
  [Symbol.for('bing')]: 4,
};

// 旧写法
Object.getOwnPropertyNames(myObject)
// ['foo', 'bar']

Object.getOwnPropertySymbols(myObject)
//[Symbol(baz), Symbol(bing)]

// 新写法
Reflect.ownKeys(myObject)
// ['foo', 'bar', Symbol(baz), Symbol(bing)]

1)当参数不是对象时:

console.log(Object.getOwnPropertyNames(111)) // []
console.log(Object.getOwnPropertyNames('111')) // ['0', '1', '2', 'length']
Reflect.ownKeys(111) // TypeError: Reflect.ownKeys called on non-object

一部分是Object的操作符改为Reflect的函数行为:

9.Reflect.has(target, name)

10.Reflect.deleteProperty(target, name)

11.Reflect.construct(target, args)

12.Reflect.get(target, name, receiver)类似obj[key],返回target对象的name属性的值,如果没有该属性,则返回undefined

var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar;
  },
}

Reflect.get(myObject, 'foo') // 1
Reflect.get(myObject, 'bar') // 2
Reflect.get(myObject, 'baz') // 3

1) 第三个参数用于当取值时该name属性为getter函数时,其this指向receiver

var myObject = {
  foo: 1,
  bar: 2,
  get baz() {
    return this.foo + this.bar;
  },
};

var myReceiverObject = {
  foo: 4,
  bar: 4,
};

Reflect.get(myObject, 'baz', myReceiverObject) // 8

 2)如果第一个参数不是对象,Reflect.get方法会报错。

Reflect.get(1, 'foo') // 报错
Reflect.get(false, 'foo') // 报错

13.Reflect.set(target, name, value, receiver)类似obj[key]=value,设置target的name属性的值为value。Reflect.set返回的布尔类型

var myObject = {
  foo: 1,
  set bar(value) {
    return this.foo = value;
  },
}

myObject.foo // 1

Reflect.set(myObject, 'foo', 2);
myObject.foo // 2

Reflect.set(myObject, 'bar', 3)
myObject.foo // 3

1)如果name属性设置了赋值函数,则赋值函数的this绑定receiver

var myObject = {
  foo: 4,
  set bar(value) {
    return this.foo = value;
  },
};

var myReceiverObject = {
  foo: 0,
};

Reflect.set(myObject, 'bar', 1, myReceiverObject);
myObject.foo // 4
myReceiverObject.foo // 1

2)注意,如果 Proxy对象和 Reflect对象联合使用,前者拦截赋值操作,后者完成赋值的默认行为,而且传入了receiver,那么Reflect.set会触发Proxy.defineProperty拦截。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值