Reflect.construct让我们做的事情之前是不可行的?

时间:2017-01-06 21:54:06

标签: javascript reflection

我正在努力找到一个很好的理由来使用Reflect.construct来实现我以前无法实现的值得注意的东西。

我不是在寻找像one here这样的答案,因为这个例子看起来并不是很有用。例如,我为什么要写

function greetingFactory(name) {
  return Reflect.construct(Greeting, [name]);
}

我什么时候可以写

function greetingFactory(name) {
  return new Greeting(name);
}

您是否知道Reflect.construct的任何显着用例?

编辑:似乎我自己也找到了一个用例,但我不确定它是否稳固,是否会崩溃,但基本上我似乎可以让new.target使用ES5风格的类通过这样编写:

function Foo() {
  console.log('Foo, new.target:', new.target)
  this.name = "foo"
}

Foo.prototype.sayHello = function sayHello() {
  return this.name
}

function Bar() {
  console.log('Bar, new.target:', new.target)
  let _ = Reflect.construct(Foo, [], new.target)
  _.name = _.name + " bar"
  return _
}

Bar.prototype = Object.create(Foo.prototype)

Bar.prototype.sayHello = function() {
  return "Hello " + Foo.prototype.sayHello.call(this) + "!"
}

function Baz() {
  console.log('Baz, new.target:', new.target)
  let _ = Reflect.construct(Bar, [], new.target)
  _.name = _.name + " baz"
  return _
}

Baz.prototype = Object.create(Bar.prototype)

Baz.prototype.sayHello = function() {
  return Bar.prototype.sayHello.call(this) + " Hello again!"
}

let baz = new Baz

console.log(baz.sayHello())

关于它的一个很酷的事情是this在原型方法中是预期的!

4 个答案:

答案 0 :(得分:1)

它接受应该在第三个参数 Reflect.constructor 中使用其原型的构造函数。 请在下面尝试以下代码,并查看 array1Proto 中的原型。 尽管开头是Object / func1,但仍将Array设置为构造函数。

    function func1(a, b, c) {
      this.sum = a + b + c;
    }
    
    const args = [1, 2, 3];
    const object1 = new func1(...args);
    const object2 = Reflect.construct(func1, args);
    const array1Proto = Reflect.construct(func1, args, Array);
    
    console.log(object1)
    console.log(object2)
    console.log(array1Proto)

答案 1 :(得分:1)

我也一直在尝试整理Reflect.construct的有用应用程序。我想我可能已经找到了一些东西,但是最好还是以类似的方式将这个想法与其他人联系起来。

我在想的是,您可以使用Reflect.construct在实例化的对象和预期的[[Prototype]]之间楔入一个[[Prototype]]。这样一来,您就可以遮盖属于预期的[[Prototype]]的属性和方法,而又不会太麻烦。

function Wedge() {};

Wedge.prototype = Object.create(String.prototype);

Wedge.prototype.toUpperCase = function () {
    return "nope";
}

let someString = Reflect.construct(String, ['Foo Bar'], Wedge)

someString.toUpperCase() // "nope"

答案 2 :(得分:1)

我知道的Reflect.construct仅有三个用例是:

  1. 在代理服务器construct trap中使用它来获取默认行为(或获取经过稍微修改的默认行为)。

  2. 在需要使用需要将元素作为离散参数传递的数组调用构造函数时,使用它来避免创建和使用迭代器。您可以可以做

    t = new Thing(...theArray);
    

    但是涉及创建和使用迭代器,而

    t = Reflect.construct(Thing, theArray);
    

    不使用迭代器,它的工作量要少得多(通常并不重要;这是在您知道时间至关重要的情况下)。 (construct只使用length而不是迭代器,而是直接访问01等属性。)

    ES2015之前这些选项都不可用。相反,您必须这样做:

    t = Thing.apply(Object.create(Thing.prototype), theArray);
    

    与ES5构造函数一起使用。 (它不适用于通过class创建的ES2015 +构造函数,但您不需要它-您可以使用上面两个选项之一。)

  3. 在构造classError或Web组件(某些人don't like to use class等)的子类型的实例时,应避免使用Array在可能需要转译的项目中是很好的论据)。 (也就是说,Reflect.construct也不能完美地填充。)

答案 3 :(得分:1)

这个话题还很晚,但是实际上可以在ES5代码中适当地子类化本机类。看起来像这样:

//ES6-class version
class MyArray extends Array {
  peekLast() {
    return this[this.length - 1];
  }
}

//ES5-class version
function MyArray() {
  //No need to use new since that instance gets discarded
  var retval = new Array();
  retval.__proto__ = MyArray.prototype; //This is the key line
  //Do any other initialization to retval here.
  return retval;
}
MyArray.prototype = Object.create(Array.prototype, {
  constructor: {
    configurable: true,
    writable: true,
    value: MyArray
  },
  peekLast: {
    configurable: true,
    writable: true,
    value: function peekLast() { return this[this.length - 1]; }
  }
});
MyArray.__proto__ = Array;

我知道__proto__在ES6之前从未出现在ES标准中,但这是所有主要浏览器和引擎都支持的事实上的Web标准。这就是使之成为可能的原因。