关闭

es6 Reflect对象

时间: 2018-12-01阅读: 975标签: 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.云服务推荐: 国内主流云服务商,各类云产品的最新活动,优惠券领取。地址:阿里云腾讯云华为云

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

js解构赋值,关于es6中的解构赋值的用途总结

ES6中添加了一个新属性解构,允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。用途:交换变量的值、从函数返回多个值、函数参数的定义、提取JSON数据、函数参数的默认值...

es6之迭代器

迭代器是被设计专用于迭代的对象,带有特定接口。所有的迭代器对象都拥有 next() 方法,会返回一个结果对象。该结果对象有两个属性:对应下一个值的 value ,以及一个布尔类型的 done

ES6 中的一些技巧,使你的代码更清晰,更简短,更易读!

ES6 中的一些技巧:模版字符串、块级作用域、Let、Const、块级作用域函数问题、扩展运算符、函数默认参数、解构、对象字面量和简明参数、动态属性名称、箭头函数、for … of 循环、数字字面量。

异步神器async-await

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

对ES6的yield示例分析

这里主要是对yield关键字的,yield实际上可以看作是一种新的中断机制,大家都知道javascript函数执行是顺序的,中途没有暂停,等待消息只能通过回调或者settimeout等延迟检查来完成。

ES6中let变量的特点,使用let声明总汇

ES6中let变量的特点:1.let声明变量存在块级作用域,2.let不能先使用再声明3.暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,4.不允许重复声明

ES6的循环和可迭代对象

本文将研究 ES6 的 for ... of 循环。在过去,有两种方法可以遍历 javascript。首先是经典的 for i 循环,它使你可以遍历数组或可索引的且有 length 属性的任何对象。

9个常用ES6特性归纳

顾名思义,用于对象或数组之前的展开操作符(…),将一个结构展开为列表。这种写法够优雅,够简洁吧?如果不用展开操作符

ES5和ES6对象导出和导入

如果模块中使用 export default {},只能通过 import 对象名称 from 模块路径 ,不能通过 import {对象名称} from 模块路径。如果使用 import {对象名称} from 模块路径 导出具体某个对象或者方法名称

jses6语法:Iterator遍历器 promise async函数

Iterator遍历器他是一种接口,为不同的数据结构体用统一的访问机制,接口部署在数据结构的Symbol.Iterator属性,或者说一个数据结构只要具备Symbol.Iterator属性就认为是可以遍历的

点击更多...

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