黑马程序员技术交流社区

标题: es6 reflect [打印本页]

作者: 绮罗    时间: 2020-4-17 10:31
标题: es6 reflect
这一篇文章,我们来学习一下ES6的Reflect这个全局对象;首先我们要了解一下,为什么会新添加这么一个全局对象?如果你看过Reflect的一些函数,你就会发现,这个对象上的方法基本上都可以从Object上面找到,找不到的那些,也是可以通过对对象命令式的操作去实现的;那么为什么还要新添加一个呢?
主要的原因有这么几点,1:Reflect上面的一些方法并不是专门为对象设计的,比如Reflect.apply方法,它的参数是一个函数,如果使用Object.apply(func)会让人感觉很奇怪。2:用一个单一的全局对象去存储这些方法,能够保持其它的JavaScript代码的整洁、干净。不然的话,这些方法可能是全局的,或者要通过原型来调用。3:将一些命令式的操作如delete,in等使用函数来替代,这样做的目的是为了让代码更加好维护,更容易向下兼容;也避免出现更多的保留字。
下面我们来学习一下Reflect这个全局的对象,Reflect对象有下面这些静态的方法:
Reflect.applyReflect.constructReflect.definePropertyReflect.deletePropertyReflect.enumerate // 废弃的Reflect.getReflect.getOwnPropertyDescriptorReflect.getPrototypeOfReflect.hasReflect.isExtensibleReflect.ownKeysReflect.preventExtensionsReflect.setReflect.setPrototypeOf
下面我们来一一讲解这些方法,了解这些方法如何使用。
Reflect.apply
Reflect.apply(target, thisArgument, argumentsList)
通过指定的参数列表,来调用函数target。其中,target就是我们的目标函数,thisArgument就是target函数调用的时候绑定的this对象,argumentsList就是函数的参数列表。这个方法与原来ES5的Function.prototype.apply类似,我们来看一下示例:
// 查找一个数字数组里面的最大元素const arr = [1, 3, 5, 7];let max;// ES6max = Reflect.apply(Math.max, undefined, arr);console.log(max);  // 7// ES5max = Math.max.apply(undefined, arr);console.log(max); // 7max = Function.prototype.apply.call(Math.max, undefined, arr);console.log(max); // 7// 截取字符串的一部分let str = 'hello, world';let newStr;// ES6newStr = Reflect.apply(String.prototype.slice, str, [2, 8]);console.log(newStr); // llo, w// ES5newStr = str.slice(2, 8);console.log(newStr); // llo, wnewStr = String.prototype.slice.apply(str, [2, 8]);console.log(newStr); // llo, w
Reflect.construct
Reflect.construct(target, argumentsList[, newTarget])
使用这个方法,与使用new target(...args)方法类似,相当于提供了一种新的不使用new来调用构造函数的方法;其中,target表示被运行的目标函数,argumentsList调用构造函数传递的参数数组或者伪数组,newTarget参数为构造函数,表示使用Reflect.construct后生成的对象是谁的实例,如果没有传递第三个参数,默认和target一样。
我们可以这样理解,如果没有传递第三个参数,那么target就是唯一的构造函数;但是如果传递了第三个参数,那就表示:我们的实例由两部分组成,实力的属性部分由第一个参数部分生成,实例的方法部分由第三个参数生成。下面我们来实践一下这个方法:
// ES5function A(name) {    console.log('Function A is invoked!');    this.name = name || 'dreamapple';}A.prototype.getName = function() {    console.log(this.name);    return this.name;};function B(age) {    console.log('Function B is invoked!');    this.age = age || 22;}B.prototype.getAge = function() {    console.log(this.age);    return this.age;};// 使用函数A作为构造函数let a = Reflect.construct(A, ['happy']);// 使用函数B作为构造函数let b = Reflect.construct(A, ['happy'], B);console.log(a);console.log(b);a.getName();b.getAge();console.log('---------');// ES6class A1 {    constructor(name) {        console.log('Class A1 is invoked!');        this.name = name || 'dreamapple';    }    getName() {        console.log(this.name);        return this.name;    }}class B1 {    constructor(age) {        console.log('Class B1 is invoked!');        this.age = age || 22;    }    getAge() {        console.log(this.age);        return this.age;    }}// 使用A1类作为构造函数let a1 = Reflect.construct(A1, ['happy']);// 使用B1类作为构造函数let b1 = Reflect.construct(A1, ['happy'], B1);console.log(a1);console.log(b1);
其中运行的结果如下图:
如果此时,我们在上面代码的输出控制台里面运行下面两个函数,则会报错:
b1.getName();// VM5221:1 Uncaught TypeError: b1.getName is not a function//    at <anonymous>:1:4b.getName();// VM5253:1 Uncaught TypeError: b.getName is not a function//    at <anonymous>:1:3
这个结果也验证了我们上面所谈论的内容,Reflect.construct会使用第三个参数来作为构造函数,如果没有第三个参数,默认使用第一个参数作为构造函数。
Reflect.defineProperty
Reflect.defineProperty(target, propertyKey, attributes)
这个方法与Object.defineProperty相似,不过Reflect.defineProperty的返回值是一个Boolean值。target表示要定义属性的对象,propertyKey表示要定义或者修改的属性名字,attributes表示定义或者被修改的属性的属性。下面我们来实践一下这个方法:
let obj = {};// 对象的属性定义失败try {    Object.defineProperty(null, 'a', {        value: 22    })}catch (e) {    console.log('define property failed!');}  // define property failed!// 使用Object.defineProperty成功的定义let obj1 = Object.defineProperty(obj, 'name', {    enumerable: true,    value: 'dreamapple'});console.log(obj); // { name: 'dreamapple' }console.log(obj1); // { name: 'dreamapple' }// 这里会返回false 因为我们上面定义name这个属性是不可修改的,// 然后我们又在这里修改了name属性,所以修改失败返回值为falselet result1 = Reflect.defineProperty(obj, 'name', {    configurable: true,    enumerable: true,    value: 'happy'});console.log(result1); // falselet result2 = Reflect.defineProperty(obj, 'age', {    configurable: true,    enumerable: true,    value: 22});console.log(result2); // trueconsole.log(obj); // { name: 'dreamapple', age: 22 }
Reflect.defineProperty与Object.defineProperty的用法是相似的,但是如果Object.defineProperty的属性定义失败了,就会抛出一个错误,成功的话就会返回这个对象;Reflect.defineProperty如果定义属性失败的话就会返回false,如果成功定义的话,就会返回true。但是如果使用Reflect.defineProperty函数,它的第一个参数不是对象的话,也会抛出错误。
Reflect.deleteProperty
Reflect.deleteProperty(target, propertyKey)
这个方法用于删除一个对象上的属性,与delete操作符相似;其中target表示要操作的对象,propertyKey表示要删除的属性。这个函数的返回值是一个Boolean值,如果成功的话,返回true;失败的话返回false。下面我们来实践这个方法:
let obj = {    name: 'dreamapple',    age: 22};let r1 = Reflect.deleteProperty(obj, 'name');console.log(r1); // truelet r2 = Reflect.deleteProperty(obj, 'name');console.log(r2); // truelet r3 = Reflect.deleteProperty(Object.freeze(obj), 'age');console.log(r3); // false
Reflect.get
Reflect.get(target, propertyKey[, receiver])
这个方法用来读取一个对象的属性,target是目标对象,propertyKey是我们要读取的属性,receiver是可选的,如果propertyKey的getter函数里面有this值,那么receiver就是这个this所代表的上下文。下面我们来实践这个方法:
let obj = {    name: 'dreamapple',    age: 22,    get money() {        console.log(`I can tell you my name ${this.name}, but not my money`);        return 0    }};console.log(Reflect.get(obj, 'name')); // dreamappleconsole.log(Reflect.get(obj, 'myName')); // undefined// I can tell you my name dreamapple, but not my money// 0console.log(Reflect.get(obj, 'money'));// I can tell you my name happy, but not my money// 0console.log(Reflect.get(obj, 'money', {name: 'happy'}));
Reflect.getOwnPropertyDescriptor
Reflect.getOwnPropertyDescriptor(target, propertyKey)
这个方法与Object.getOwnPropertyDescriptor方法类似,其中target是目标对象,propertyKey是对象的属性,如果这个属性存在属性描述符的话就返回这个属性描述符;如果不存在的话,就返回undefined。下面我们来实践一下这个方法:
let obj = {};Reflect.defineProperty(obj, 'name', {    configurable: true,    enumerable: true,    writable: true,    value: 'dreamapple'});let descriptor = Reflect.getOwnPropertyDescriptor(obj, 'name');// { value: 'dreamapple',//   writable: true,//   enumerable: true,//   configurable: true// }console.log(descriptor);let d1 = Reflect.getOwnPropertyDescriptor(obj, 'age');console.log(d1); // undefined// 如果第一个参数不是对象let d2 = Object.getOwnPropertyDescriptor('0', 'name');console.log(d2); // undefinedtry {    let d3 = Reflect.getOwnPropertyDescriptor('0', 'name');    console.log(d3);} catch (e) {    console.log('error');} // error
这个方法与Object.getOwnPropertyDescriptor有一些不同的地方,如果第一个参数不是对象的话,那么Object.getOwnPropertyDescriptor会将这个参数强制转换为对象,而方法 Reflect.getOwnPropertyDescriptor会抛出一个错误。
Reflect.getPrototypeOf
Reflect.getPrototypeOf(target)
这个方法与Object.getPrototypeOf方法是一样的,都是返回一个对象的原型,也就是内部的[[Prototype]]属性的值。下面我们来实践一下这个方法:
// ES5function A() {}A.prototype.sayHello = function(){};var a = new A();var aPrototype = Object.getPrototypeOf(a);console.log(aPrototype);// Object//  constructor: A()//  sayHello: ()//  __proto__: Object// ES6let ap = Reflect.getPrototypeOf(a);console.log(ap);// Object//  constructor: A()//  sayHello: ()//  __proto__: Objectconsole.log(ap === aPrototype); // true
从上面的结果中,我们可以看到;对于同一个对象,使用Reflect.getPrototypeOf方法和使用Object.getPrototypeOf方法返回的结果是一致的。这里面还有一些需要注意的事情,如果我们要获取原型的那个值不是一个对象,那么函数Reflect.getPrototypeOf会抛出一个异常;对于给定对象的原型,如果没有继承的属性,则返回null。
Reflect.has
Reflect.has(target, propertyKey)
这个方法相当于ES5的in操作符,就是检查一个对象上是否含有特定的属性;我们继续来实践这个方法:
function A(name) {    this.name = name || 'dreamapple';}A.prototype.getName = function() {    return this.name;};var a = new A();console.log('name' in a); // trueconsole.log('getName' in a); // truelet r1 = Reflect.has(a, 'name');let r2 = Reflect.has(a, 'getName');console.log(r1, r2); // true true
这个函数的返回结果是一个Boolean值,如果存在就返回true,不存在就返回false。当然如果目标对象(target)不是一个对象,那么就会抛出一个异常。
Reflect.isExtensible
Reflect.isExtensible(target)
这个函数检查一个对象是否是可以扩展的,也就是是否可以添加新的属性;和方法Object.isExtensible方法相似。其中,target表示目标对象,如果这个目标对象不是一个对象,那么函数会抛出一个异常;函数的返回值表示这个对象是否可以扩展;如果是true,表示这个对象可以扩展,如果是false,表示这个对象不可以扩展。下面我们来实践这个方法:
let obj = {};let r1 = Reflect.isExtensible(obj);console.log(r1); // true// 密封这个对象Object.seal(obj);let r2 = Reflect.isExtensible(obj);console.log(r2); // false// 冻结一个对象let obj1 = Object.freeze({});let r3 = Reflect.isExtensible(obj1);console.log(r3); // false// 阻止一个对象添加新的属性let obj2 = {};Object.preventExtensions(obj2);let r4 = Reflect.isExtensible(obj2);console.log(r4); // false// Reflect.isExtensible 与 Object.isExtensible的区别try{    Reflect.isExtensible(1);}catch(e) {    // 这里捕获错误    console.log(e); // Reflect.isExtensible called on non-object...}try{    Object.isExtensible(1);}catch(e) {    console.log(e);}
从上面的实践中,我们可以看出;当使用Object.isExtensible函数时,如果目标对象不是一个对象,那么这个函数会把这个值强制转换成对象,从而不会抛出错误;但是使用Reflect.isExtensible方法就会抛出一个错误,因为它要求目标对象必须是一个对象。
Reflect.ownKeys
Reflect.ownKeys(target)
这个函数的作用是,返回由目标对象自身的属性键组成的数组;其中target表示目标对象,如果这个目标对象不是一个对象那么这个函数就会抛出一个异常。这个数组的值等于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target)),下面我们来实践这个方法:
let a = Symbol.for('a');let b = Symbol.for('b');let obj = {    [a]: 10,    : 20,    key1: 30,    key2: 40};let arr1 = Object.getOwnPropertyNames(obj);console.log(arr1); // [ 'key1', 'key2' ]let arr2 = Object.getOwnPropertySymbols(obj);console.log(arr2); // [ Symbol(a), Symbol(b) ]let arr3 = Reflect.ownKeys(obj);console.log(arr3); // [ 'key1', 'key2', Symbol(a), Symbol(b) ]
Reflect.preventExtensions
Reflect.preventExtensions(target)
这个方法与Object.preventExtensions方法相似,不同的是对于方法Reflect.preventExtensions,传递的参数必须是一个对象,否则会抛出一个异常;但是对于函数Object.preventExtensions,如果我们传递的值不是一个对象,那么它会强制把这个值转换成一个对象,所以不会抛出异常。这个函数的作用是,阻止新的属性添加到对象中去。
下面我们来实践一下这个方法:
let obj = {};let r1 = Reflect.isExtensible(obj);console.log(r1); // trueReflect.preventExtensions(obj);let r2 = Reflect.isExtensible(obj);console.log(r2); // false
Reflect.set
Reflect.set(target, propertyKey, value[, receiver])
这个函数的作用是在一个对象身上设置一个属性,其中target表示我们要操作的对象;propertyKey表示我们要设置的属性名,value表示我们要设置的属性值,receiver表示的是一个this值,如果我们在设置值的时候遇到setter函数,那么这个receiver值表示的就是setter函数中的this值。这个函数会返回一个Boolean值,表示在目标对象上设置属性是否成功。
下面我们来实践一下这个函数:
let obj = {    set name(name) {        console.log('this: --> ', this);    },    age: 22};let r1 = Reflect.set(obj, 'age', 24);console.log(r1); // trueconsole.log(obj); // { name: [Setter], age: 24 }console.log('\n');let r2 = Reflect.set(obj, 'name', 'dreamapple', {test: 'test'}); // this: -->  { test: 'test' }console.log(r2); // trueconsole.log(obj); // { name: [Setter], age: 24 }
Reflect.setPrototypeOf
Reflect.setPrototypeOf(target, prototype)
Reflect.setPrototypeOf与Object.setPrototypeOf方法的作用是相似的,设置一个对象的原型,如果设置成功的话,这个对象会返回一个true;如果设置失败,这个对象会返回一个false。下面我们来实践一下这个方法:
let obj = {};let r1 = Reflect.setPrototypeOf(obj, Object.prototype);console.log(r1); // truelet r2 = Reflect.setPrototypeOf(Object.freeze({}), null);console.log(r2); // false
到这里为止,我们把关于Reflect的静态方法都实践了一遍,希望可以帮助到大家;当然这篇文章也许在个别地方会有错误,也欢迎大家指正。


作者: xiaowang66    时间: 2020-4-23 16:18
加油啊
作者: 放荡的人生    时间: 2020-4-23 17:13
加油




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2