ES6 Reflect深入学习

概述

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法,且不是一个函数对象,因此它是不可构造的。

Reflect的所有属性和方法都是静态的。

Reflect 可以用于获取目标对象的行为,它与Object类似,但是更易读,为操作对象提供了一种更优雅的方式。

ES6中将Object的一些明显属于语言内部的方法一直到了Reflect对象上(当前某些方法同时存在于Object和Reflect对象上),未来的新方法只会部署在Reflect对象上。Reflect对象使用函数的方式实现了Object的命令式操作。

主体

静态方法

  • Reflect.apply(target, thisArg, arg)

    对一个函数进行调用操作,同时传入一个数组作为调和参数,与Fuction.prototype.apply()功能类似。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let arr = [1,2,3]
    Reflect.apply(Array.prototype.push, arr, [1000]); // [1,2,3,1000]

    class Person {
    static lived (addr) {
    return addr
    }
    getAge (val) {
    return '今年' + val + '岁了'
    }
    }
    Reflect.apply(Person.prototype.getAge, undefined, [20]); // "今年20岁了"
    Reflect.apply(Person.lived, undefined, ['earth']); // "earth"
  • Reflect.construct(target, argumentsList[, newTarget])

    对构造函数进行new操作,相当于执行 new target(...args),如果target或newTarget不是构造函数,抛异常。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    function OneClass() {
    console.log('OneClass');
    console.log(new.target);
    console.log(this.aa);
    }
    function OtherClass() {
    console.log('OtherClass');
    console.log(new.target);
    }
    OtherClass.prototype.aa = 'aa'

    var obj1 = Reflect.construct(OneClass, args); // 继承的是OneClass
    // 输出:
    // OneClass
    // function OneClass { ... }

    // new.target值会自动指定到target(或者newTarget,前提是newTarget指定了)。
    var obj2 = Reflect.construct(OneClass, args, OtherClass); // 继承的是OtherClass
    // 执行的是OneClass,this指向的是obj2的原型
    // 输出:
    // OneClass
    // function OtherClass { ... }
    // 'aa'

    var obj3 = Object.create(OtherClass.prototype);
    OneClass.apply(obj3, args); // 执行的是OneClass,this指向的是obj3的原型
    // 输出:
    // OneClass
    // undefined
    // 'aa'
  • Reflect.defineProperty(target, propertyKey, attributes)

    Object.defineProperty() 类似。如果设置成功就会返回 true

    区别:Object.defineProperty 返回一个对象,或者如果属性没有被成功定义,抛出一个 TypeError 。 相比之下,Reflect.defineProperty方法只返回一个 Boolean ,来说明该属性是否被成功定义。

    1
    2
    3
    let obj = {}
    Reflect.defineProperty(obj, 'x', {value: 7}) // true
    obj.x // 7

    在检查属性是否成功被定义时

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 如果是Object.defineProperty,则
    try {
    Object.defineProperty(target, property, attributes)
    } catch (err) {
    throw TypeError('类型错误')
    }

    // 如果是Reflect.defineProperty,则只能
    if (Reflect.defineProperty(target, property, attributes)) {
    // 成功
    } else {
    // 失败
    }
  • Reflect.deleteProperty(target, propertyKey)

    静态方法 Reflect.deleteProperty 允许用于删除属性。跟delete obj.xxx类似。如果删除成功就会返回 true

    1
    2
    3
    4
    5
    // 如果属性不存在,返回 true
    Reflect.deleteProperty({}, "foo"); // true

    // 如果属性不可配置,返回 false
    Reflect.deleteProperty(Object.freeze({foo: 1}), "foo"); // false
  • Reflect.get(target, propertyKey[, receiver])

    Reflect.get方法与从 对象 (target[propertyKey]) 中读取属性类似,但它是通过一个函数执行来操作的。

    如果target对象中指定了getterreceiver则为getter调用时的this值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // Object
    var obj = { x: 1, y: 2 };
    Reflect.get(obj, "x"); // 1

    // Proxy with a get handler
    var x = {p: 1};
    var obj = new Proxy(x, {
    get(target, key, receiver) {
    return key + "bar";
    }
    });
    // 会被Proxy代理拦截,执行get
    Reflect.get(obj, "foo"); // "foobar"
  • Reflect.set(target, propertyKey, value[, receiver])

    Reflect.set() 工作方式就像在一个对象上设置一个属性。

    如果遇到 setterreceiver则为setter调用时的this值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // Object
    var obj = {};
    Reflect.set(obj, "prop", "value"); // true
    obj.prop; // "value"

    // With just one argument, propertyKey and value are "undefined".
    var obj = {};
    Reflect.set(obj); // true
    Reflect.getOwnPropertyDescriptor(obj, "undefined");
    // { value: undefined, writable: true, enumerable: true, configurable: true }
  • Reflect.getOwnPropertyDescriptor(target, propertyKey)

    Object.getOwnPropertyDescriptor()方法相似。如果在对象中存在,则返回给定的属性的属性描述符。否则返回 undefined。

    1
    2
    3
    4
    5
    Reflect.getOwnPropertyDescriptor({x: "hello"}, "x");
    // {value: "hello", writable: true, enumerable: true, configurable: true}

    Reflect.getOwnPropertyDescriptor({x: "hello"}, "y");
    // undefined

    如果该方法的第一个参数不是一个对象(一个原始值),那么将造成 TypeError 错误。而对于 Object.getOwnPropertyDescriptor,非对象的第一个参数将被强制转换为一个对象处理。

    1
    2
    3
    4
    5
    Reflect.getOwnPropertyDescriptor("foo", 0);
    // TypeError: "foo" is not non-null object

    Object.getOwnPropertyDescriptor("foo", 0);
    // { value: "f", writable: false, enumerable: true, configurable: false }
  • Reflect.getPrototypeOf(target)

    静态方法 Reflect.getPrototypeOf()Object.getPrototypeOf() 方法几乎是一样的。都是返回指定对象的原型(即内部的 [[Prototype]] 属性的值)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 如果参数为 Object,返回结果相同
    Object.getPrototypeOf({}) // Object.prototype
    Reflect.getPrototypeOf({}) // Object.prototype

    // 在 ES5 规范下,对于非 Object,抛异常
    Object.getPrototypeOf('foo') // Throws TypeError
    Reflect.getPrototypeOf('foo') // Throws TypeError

    // 在 ES2015 规范下,Reflect 抛异常, Object 强制转换非 Object
    Object.getPrototypeOf('foo') // String.prototype
    Reflect.getPrototypeOf('foo') // Throws TypeError

    // 如果想要模拟 Object 在 ES2015 规范下的表现,需要强制类型转换
    Reflect.getPrototypeOf(Object('foo')) // String.prototype
  • Reflect.setPrototypeOf(target, prototype)

    除了返回类型以外,静态方法 Reflect.setPrototypeOf()Object.setPrototypeOf() 方法是一样的。它可设置对象的原型(即内部的 [[Prototype]] 属性)为另一个对象或 null,如果操作成功返回 true,否则返回 false

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Reflect.setPrototypeOf({}, Object.prototype); // true

    // It can change an object's [[Prototype]] to null.
    Reflect.setPrototypeOf({}, null); // true

    // Returns false if target is not extensible.
    Reflect.setPrototypeOf(Object.freeze({}), null); // false

    // Returns false if it cause a prototype chain cycle.
    var target = {};
    var proto = Object.create(target);
    Reflect.setPrototypeOf(target, proto); // false
  • Reflect.has(target, propertyKey)

    静态方法 Reflect.has() 作用与 in 操作符 相同。

    1
    2
    3
    4
    5
    6
    // Proxy 对象的 .has() 句柄方法
    obj = new Proxy({}, {
    has(t, k) { return k.startsWith("door"); }
    });
    Reflect.has(obj, "doorbell"); // true
    Reflect.has(obj, "dormitory"); // false
  • Reflect.isExtensible(target)

    静态方法 Reflect.isExtensible() 判断一个对象是否可扩展 (即是否能够添加新的属性)。与它 Object.isExtensible() 方法相似,但有一些不同,详情可见 differences

    首先区分一下Object的preventExtensions,seal,freeze等冻结属性的方法的区别

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    const a = { prop: 1 };
    const b = { prop: 1 };
    const c = { prop: 1 };
    Object.preventExtensions(a);
    Object.seal(b);
    Object.freeze(c);
    Object.getOwnPropertyDescriptors(a);
    // { prop: {value:1, writable: true, enumerable: true, configurable: true} }
    Object.getOwnPropertyDescriptors(b);
    // { prop: {value:1, writable: true, enumerable: true, configurable: false} }
    Object.getOwnPropertyDescriptors(c);
    // { prop: {value:1, writable: false, enumerable: true, configurable: false} }
    // configurable: true 当且仅当此属性描述符的类型可以更改,并且该属性可以从相应的对象中删除时
    // enumerable: true 当且仅当在枚举相应对象的属性时显示此属性
    // writable: true 当且仅当与属性关联的值可以更改时(仅数据描述符)
    // value: undefined 与属性关联的值(仅数据描述符)

    a.prop = 2;
    a.key = 0;
    a // { prop: 2 }
    delete a.prop
    a // {}

    b.prop = 2;
    b.key = 0;
    b // { prop: 2 }
    delete b.prop
    b // { prop: 2 }

    c.prop = 2;
    c.key = 0;
    c // { prop: 1 }
    delete a.prop
    c // { prop: 1 }

    console.log(Object.isExtensible(a)); // false
    console.log(Object.isExtensible(b)); // false
    console.log(Object.isExtensible(c)); // false

    关于冻结对象小结:

    1. Object.preventExtensions() 方法让一个对象变的不可扩展,也就是永远不能再添加新的属性。
    2. Object.seal() 方法封闭一个对象,阻止添加新属性并将所有现有属性标记为不可配置。当前属性的值只要原来是可写的就可以改变。
    3. Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

    言归正传,当Reflect.isExtensible()判断这些冻结属性时会咋样?

    跟上述的Object.isExtensible(a)的结果其实是一样的。那么Object.isExtensible与Reflect.isExtensible()有啥区别。如下案例:

    1
    2
    3
    4
    5
    6
    7
    Reflect.isExtensible(1);
    // TypeError: 1 is not an object

    Object.isExtensible(1);
    // false

    // Reflect.isExtensible匹配的参数如果不是一个对象,则会抛异常;而Object.isExtensible匹配到非对象,费对象的第一个参数会被强制转化为对象
  • Reflect.preventExtensions(target)

    静态方法 Reflect.preventExtensions() 方法阻止新属性添加到对象 (例如:防止将来对对象的扩展被添加到对象中)

    preventExtensions的用法和isExtensible基本一致,不再赘述。

  • Reflect.ownKeys(target)

    静态方法 Reflect.ownKeys() 返回一个由目标对象自身的属性键组成的数组。它的返回值等同于Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Reflect.ownKeys({z: 3, y: 2, x: 1}); // [ "z", "y", "x" ]
    Reflect.ownKeys([]); // ["length"]

    var sym = Symbol.for("comet");
    var sym2 = Symbol.for("meteor");
    var obj = {[sym]: 0, "str": 0, "773": 0, "0": 0,
    [sym2]: 0, "-1": 0, "8": 0, "second str": 0};
    Reflect.ownKeys(obj); // 不包括原型
    // [ "0", "8", "773", "str", "-1", "second str", Symbol(comet), Symbol(meteor) ]