javascript中的动态对象构造?

ein*_*nus 18 javascript dynamic-languages

当我想在javascript中使用从其他地方提供的参数调用函数时,我可以使用apply函数的方法,如:

array = ["arg1", 5, "arg3"] 
...
someFunc.apply(null, array);
Run Code Online (Sandbox Code Playgroud)

但是如果我需要以类似的方式调用构造函数呢?这似乎不起作用:

array = ["arg1", 5, "arg3"] 
...
someConstructor.apply({}, array);
Run Code Online (Sandbox Code Playgroud)

至少不是我在尝试:

template = ['string1', string2, 'etc'];
var resultTpl = Ext.XTemplate.apply({}, template);
Run Code Online (Sandbox Code Playgroud)

这不起作用:

Ext.XTemplate.prototype.constructor.apply({}, template);
Run Code Online (Sandbox Code Playgroud)

有什么办法使这个工作吗?(在这个特殊情况下,我发现它new Ext.XTemplate(template)会起作用,但我对一般情况感兴趣)

类似的问题,但特定于内置类型,没有我可以使用的答案: 通过调用prototype.constructor.apply实例化JavaScript对象

谢谢.

编辑:

时间已经过去,ES6和转换器现在已成为一件事.在ES6中,做我想做的事是微不足道的:new someConstructor(...array). Babel会将其转换为ES5 new (Function.prototype.bind.apply(someConstructor, [null].concat(array)))();,如何构建JavaScript对象(使用'apply')中进行了解释?.

T.J*_*der 35

使用构造函数没有简单,直接的方法.这是因为当您使用new关键字调用构造函数时会发生特殊情况,因此如果您不打算这样做,则必须模拟所有这些特殊事物.他们是:

  1. 创建一个新的对象实例(你正在这样做).
  2. 将该对象的内部原型设置为构造函数的prototype属性.
  3. 设置该对象的constructor属性.
  4. 使用该对象实例调用构造函数作为this值(您正在这样做).
  5. 处理构造函数的特殊返回值.

认为这是关于它的,但值得在规范中仔细检查.

所以,如果你可以避免它,只是直接使用构造函数,我会这样做.:-)如果你不能,你仍然可以做到这一点,它只是尴尬,涉及变通方法.(另请参阅StackOverflow上的相关答案,虽然我在这里覆盖了所有的地方[然后是一些].)

你最大的问题是上面的#2:设置对象的内部原型.很长一段时间,没有标准的方法来做到这一点.有些浏览器支持__proto__这样做的属性,所以你可以使用它,如果有的话.好消息是ECMAScript 5引入了一种明确的方法:Object.create.因此像Chrome这样的尖端浏览器就是这样.但是如果你正在处理既没有Object.create也没有的浏览器__proto__,那就有点难看了:

1)定义自定义构造函数.

2)将其prototype属性设置prototype为实际构造函数的属性

3)使用它来创建一个空白对象实例.

那为你处理原型.然后你继续:

4)用constructor真实的构造函数替换该实例上的属性.

5)通过调用真正的构造函数apply.

6)如果真实构造函数的返回值是一个对象,则使用它而不是你创建的对象; 否则,使用您创建的那个.

像这样的东西(实例):

function applyConstruct(ctor, params) {
    var obj, newobj;

    // Use a fake constructor function with the target constructor's
    // `prototype` property to create the object with the right prototype
    function fakeCtor() {
    }
    fakeCtor.prototype = ctor.prototype;
    obj = new fakeCtor();

    // Set the object's `constructor`
    obj.constructor = ctor;

    // Call the constructor function
    newobj = ctor.apply(obj, params);

    // Use the returned object if there is one.
    // Note that we handle the funky edge case of the `Function` constructor,
    // thanks to Mike's comment below. Double-checked the spec, that should be
    // the lot.
    if (newobj !== null
        && (typeof newobj === "object" || typeof newobj === "function")
       ) {
        obj = newobj;
    }

    // Done
    return obj;
}
Run Code Online (Sandbox Code Playgroud)

你可以把它更进一步,如果需要的话只能使用假构造,看看是否Object.create还是__proto__先支持,像这样(活生生的例子):

function applyConstruct(ctor, params) {
    var obj, newobj;

    // Create the object with the desired prototype
    if (typeof Object.create === "function") {
        // ECMAScript 5 
        obj = Object.create(ctor.prototype);
    }
    else if ({}.__proto__) {
        // Non-standard __proto__, supported by some browsers
        obj = {};
        obj.__proto__ = ctor.prototype;
        if (obj.__proto__ !== ctor.prototype) {
            // Setting it didn't work
            obj = makeObjectWithFakeCtor();
        }
    }
    else {
        // Fallback
        obj = makeObjectWithFakeCtor();
    }

    // Set the object's constructor
    obj.constructor = ctor;

    // Apply the constructor function
    newobj = ctor.apply(obj, params);

    // If a constructor function returns an object, that
    // becomes the return value of `new`, so we handle
    // that here.
    if (typeof newobj === "object") {
        obj = newobj;
    }

    // Done!
    return obj;

    // Subroutine for building objects with specific prototypes
    function makeObjectWithFakeCtor() {
        function fakeCtor() {
        }
        fakeCtor.prototype = ctor.prototype;
        return new fakeCtor();
    }
}
Run Code Online (Sandbox Code Playgroud)

在Chrome 6上,以上用途Object.create; 在Firefox 3.6和Opera上,它使用__proto__.在IE8上,它使用伪构造函数.

以上是相当缺乏的,但它主要处理我在这方面所知的问题.


小智 5

来自developer.mozilla:绑定函数自动适合与 new 运算符一起使用来构造由目标函数创建的新实例。当绑定函数用于构造值时,提供的 this 将被忽略。但是,提供的参数仍然被添加到构造函数调用之前。

也就是说,我们仍然需要使用 apply 从数组中获取参数并进入绑定调用。此外,我们还需要将 bind 的函数重置为 apply 函数的 this 参数。这为我们提供了一个非常简洁的单行代码,可以完全满足需要。

function constructorApply(ctor, args){
    return new (ctor.bind.apply(ctor, [null].concat(args)))();
};
Run Code Online (Sandbox Code Playgroud)