es6 Reflect对象

更新日期: 2018-12-01阅读量: 1256标签: es6

Reflect是ES6为操作对象而提供的新API,而这个API设计的目的只要有:

  • 将Object对象的一些属于语言内部的方法放到Reflect对象上,从Reflect上能拿到语言内部的方法。如:Object.defineProperty
  • 修改某些object方法返回的结果。如:Object.defineProperty(obj, name, desc)在无法定义属性的时候会报错,而Reflect.defineProperty(obj, name, desc)则会返回false
  • 让Object的操作都变成函数行为。如object的命令式:name in obj和delete obj[name] 则与 Reflect.has(obj, name)、Reflect.deleteProperty(obj, name)相等
  • Reflect对象的方法与Proxy对象的方法一一对应,只要proxy对象上有的方法reflect也能找到。
Proxy(target, {
  set: function(target, name, value, receiver) {
    var success = Reflect.set(target,name, value, receiver);
    if (success) {
      log('property ' + name + ' on ' + target + ' set to ' + value);
    }
    return success;
  }
});
var loggedObj = new Proxy(obj, {
  get(target, name) {
    console.log('get', target, name);
    return Reflect.get(target, name);
  },
  deleteProperty(target, name) {
    console.log('delete' + name);
    return Reflect.deleteProperty(target, name);
  },
  has(target, name) {
    console.log('has' + name);
    return Reflect.has(target, name);
  }
});

有了Reflect对象,很多操作会更易读

// 老写法
Function.prototype.apply.call(Math.floor, undefined, [1.75]) // 1

// 新写法
Reflect.apply(Math.floor, undefined, [1.75]) // 1

 

Reflect一共有13个静态方法

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)

上面这些方法的作用大部分与Object对象的同名方法都是相同的,与Proxy对象的方法一一对应的。

 

Reflect.get(target, name, receiver)

Reflect.get方法查找并返回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

如果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

如果第一个参数不是对象,则Reflect.get则会报错。


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

Reflect.set方法设置target对象的name属性等于value。

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

如果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

如果Proxy与Reflect联合使用,前者完成拦截赋值操作,后者完成赋值默认行为,而且传入了receiver,则Reflect.set会触发Proxy.defineProperty拦截。

let p = {
  a: 'a'
};

let handler = {
  set(target, key, value, receiver) {
    console.log('set');
    Reflect.set(target, key, value, receiver)
  },
  defineProperty(target, key, attribute) {
    console.log('defineProperty');
    Reflect.defineProperty(target, key, attribute);
  }
};

let obj = new Proxy(p, handler);
obj.a = 'A';
// set
// defineProperty

如果不传,则Proxy不会触发defineProperty拦截。

let p = {
  a: 'a'
};

let handler = {
  set(target, key, value, receiver) {
    console.log('set');
    Reflect.set(target, key, value)
  },
  defineProperty(target, key, attribute) {
    console.log('defineProperty');
    Reflect.defineProperty(target, key, attribute);
  }
};

let obj = new Proxy(p, handler);
obj.a = 'A';
// set

如果第一个参数不是对象,则Reflect.set会报错。


Reflect.has(obj, name)

Reflect.has对应 name in obj 里面的in操作

var myObject = {
  foo: 1,
};

// 旧写法
'foo' in myObject // true

// 新写法
Reflect.has(myObject, 'foo') // true

如果第一个参数不是对象,Reflect.has和in都会报错。


Reflect.deleteProperty(obj, name)

Reflect.deleteProperty方法等同于delete obj[name],用于删除对象属性。

const myObj = { foo: 'bar' };

// 旧写法
delete myObj.foo;

// 新写法
Reflect.deleteProperty(myObj, 'foo');

该方法返回一个布尔值。如果删除成功或删除的属性不存在,则返回true,如果删除失败,删除的属性依然还在,则返回false。


Reflect.construct(target, args)

Reflect.construct方法等同于new target(...args),这提供了一种不使用new,来调用构造函数的方法。

function Greeting(name) {
  this.name = name;
}

// new 的写法
const instance = new Greeting('张三');

// Reflect.construct 的写法
const instance = Reflect.construct(Greeting, ['张三']);

Reflect.getPrototypeOf(obj)

Reflect.getPrototypeOf方法用读取对象的__proto__属性,对应Object.getPrototypeOf(obj)方法。

const myObj = new FancyThing();

// 旧写法
Object.getPrototypeOf(myObj) === FancyThing.prototype;

// 新写法
Reflect.getPrototypeOf(myObj) === FancyThing.prototype;

它们的区别是,如果参数不是对象,Object.getPrototypeOf会将参数转化为对象,而Reflect.getPrototypeOf会报错。


Reflect.setPrototypeOf(obj, newProto)

Reflect.setPrototypeOf方法是设置对象的__proto__属性,返回第一个参数对象,对应Object.setPrototypeOf(obj, newProto

const myObj = new FancyThing();

// 旧写法
Object.setPrototypeOf(myObj, OtherThing.prototype);

// 新写法
Reflect.setPrototypeOf(myObj, OtherThing.prototype);

如果第一个参数不是对象,Object.setPrototypeOf会返回第一个参数对象,而Reflect.setPrototypeOf会报错。

如果第一个参数是undefined或null,则两个都会报错。


Reflect.apply(func, thisArgs, args)

Reflect.apply等同于Function.prototype.apply.call(func, thisArgs, 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 youngest = Reflect.apply(Math.min, Math, ages);
const oldest = Reflect.apply(Math.max, Math, ages);
const type = Reflect.apply(Object.prototype.toString, youngest, []);


Reflect.defineProperty(target, propertyKey, attributes)

Reflect.defineProperty等同于Object.defineProperty,用来为对象定义属性。未来后者会被逐渐废除。

function MyDate() {
  /*…*/
}

// 旧写法
Object.defineProperty(MyDate, 'now', {
  value: () => Date.now()
});

// 新写法
Reflect.defineProperty(MyDate, 'now', {
  value: () => Date.now()
});

如果第一个参数不是对象,就会抛出错误信息。

该方法配合Proxy.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"

上面代码中,Proxy.defineProperty对属性赋值设置了拦截,Reflect.defineProperty完成了赋值。


Reflect.getOwnPropertyDescriptor(target, propertyKey) 

Reflect.getOwnPropertyDescriptor方法等同于Object.getOwnPropertyDescriptor,用于得到指定属性的描述对象,将来会代替后者。

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

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

// 新写法
var theDescriptor = Reflect.getOwnPropertyDescriptor(myObject, 'hidden');

如果第一个参数不是对象Object.getOwnPropertyDescriptor不报错,返回undefined,而Reflect.getOwnPropertyDescriptor会报错,表示参数非法。

 

Reflect.isExtensible (target) 

Reflect.isExtensible等同于Object.isExtensible,返回一个布尔值,表示当前对象是否可扩展。

const myObject = {};

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

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

如果参数不是对象,Object.isExtensible会返回false,因为本来就是不可扩展的,而Reflect.isExtensible则报错。


Reflect.preventExtensions(target)

Reflect.preventExtensions等同于Object.preventExtensions,用于让一个对象变为不可扩展,返回一个布尔值,表示是否操作成功。

var myObject = {};

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

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

如果参数不是对象,Object.preventExtensions在ES5下会报错,ES6下会返回传入的值,Reflect.preventExtensions则会报错。


Reflect.ownKeys (target)

Reflect.ownKeys方法用于返回对象的所有属性,等同于Object.getOwnPropertyNames与Object.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)]


实例:使用Proxy实现观察者模式

观察者模式(Obsever mode)指的是函数自动观察数据变化,一旦对象有变化,函数会自动执行。

const person = observable({
  name: '张三',
  age: 20
});

function print() {
  console.log(`${person.name}, ${person.age}`)
}

observe(print);
person.name = '李四';
// 输出
// 李四, 20

上面代码中,数据对象person是观察目标,函数print是观察者,一旦person变化,print则自动执行。

下面使用Proxy写一个最简单的观察者模式,即实现observable和observe这两个函数。思路observable函数返回一个原始对象Proxy代理,拦截赋值操作,c触发充当观察者的各个函数。

const queuedObservers = new Set();

const observe = fn => queuedObservers.add(fn);
const observable = obj => new Proxy(obj, {set});

function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver);
  queuedObservers.forEach(observer => observer());
  return result;
}

上面代码中,先定义一个set集合,所有观察者函数都放进这个集合。然后,observable函数返回原始对象的代理,拦截赋值操作。拦截函数set中,会自动执行所有观察者。


原文链接:http://es6.ruanyifeng.com/#docs/reflect


站长推荐

1.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

链接: https://www.fly63.com/article/detial/1467

ES6 Decorator_js中的装饰器函数

ES6装饰器(Decorator)是一个函数,用来修改类的行为 在设计阶段可以对类和属性进行注释和修改。从本质上上讲,装饰器的最大作用是修改预定义好的逻辑,或者给各种结构添加一些元数据。

es6数据变更同步到视图层

数据变更同步到视图层有一个很重要的东西就是Proxy,Proxy的作用就是可以隐藏真正的对象,而用户去修改它的代理对象.Proxy可以监听数据的变化,例如

异步神器async-await

ES5的回调使我们陷入地狱,ES6的Promise使我们脱离魔障,终于、ES7的async-await带我们走向光明。今天就来学习一下 async-await。

es6 class

构造函数的prototype属性,在 ES6 的“类”上面继续存在。事实上,类的所有方法都定义在类的prototype属性上面。

在使用es6语法class的时候,babel到底做了什么?

自从有了webpack之后,我们这些jscoder似乎得到了前所未有的解放,箭头函数,对象解构,let,const关键字,以及class、extends等等,webpack会帮我们把这些es6代码转换成浏览器能够识别的es5代码,那么有多少人真正的看过,babel转换之后的代码呢?

ES6 Iterators

本文旨在分析理解 Iterators。 Iterators 是 JS中的新方法,可以用来循环任意集合。 在ES6中登场的Iterators。因其可被广泛使用,并且已在多处场景派上用场,

24个常用的es6方法

本文主要介绍 24 中 es6 方法,这些方法都挺实用的,本本请记好,时不时翻出来看看。如何检查元素是否具有指定的类?页面DOM里的每个节点上都有一个classList对象,程序员可以使用里面的方法新增、删除、修改节点上的CSS类。

es6 迭代器和生成器

可迭代对象指的是,可通过 for/of 循环的对象,是es6的特性,包括(数组,字符串,set对象,map对象);扩展操作符...可以展开可迭代对象;迭代器可用于解构赋值

ES6新特性:JavaScript中Set和WeakSet类型的数据结构

ES6提供了新的数据结构Set,Set对象不是数组, 可以用来保存对象或者基本类型, 所有保存的值都是唯一的, chrome浏览器>38和FF>13,以及nodeJS,对Set支持良好, 以下的一些代码,都可以拷贝到控制台直接运行哦;

javascript es6是什么?

ES6就是ECMAScript6是新版本JavaScript语言的标准。已经在2015年6月正式发布了。Mozilla公司将在这个标准的基础上,推出JavaScript 2.0。ECMAScript6在保证向下兼容的前提下

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!