JavaScript 中的动态对象构造?
当我想使用从其他地方提供的参数调用 javascript 中的函数时,我可以使用该函数的 apply
方法,例如:
array = ["arg1", 5, "arg3"]
...
someFunc.apply(null, array);
但是如果我需要以类似的方式调用构造函数怎么办?这似乎不起作用:
array = ["arg1", 5, "arg3"]
...
someConstructor.apply({}, array);
至少不像我尝试的那样:
template = ['string1', string2, 'etc'];
var resultTpl = Ext.XTemplate.apply({}, template);
这不起作用枯萎:
Ext.XTemplate.prototype.constructor.apply({}, template);
有什么方法可以使其起作用吗? (在这种特殊情况下,我发现 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”)?。
When I want to call a function in javascript with arguments supplied from elsewhere I can use the apply
method of the function like:
array = ["arg1", 5, "arg3"]
...
someFunc.apply(null, array);
but what if I need to call a constructor in a similar fashion? This does not seem to work:
array = ["arg1", 5, "arg3"]
...
someConstructor.apply({}, array);
at least not as I am attempting:
template = ['string1', string2, 'etc'];
var resultTpl = Ext.XTemplate.apply({}, template);
this does not work wither:
Ext.XTemplate.prototype.constructor.apply({}, template);
Any way to make that one work? (In this particular case I found that new Ext.XTemplate(template)
will work, but I am interested in the general case)
similar question but specific to built-in types and without an answer I can use:
Instantiating a JavaScript object by calling prototype.constructor.apply
Thank you.
Edit:
Time has passed and ES6 and transpilers are now a thing.
In ES6 it is trivial to do what I wanted: new someConstructor(...array)
.
Babel will turn that into ES5 new (Function.prototype.bind.apply(someConstructor, [null].concat(array)))();
which is explained in How to construct JavaScript object (using 'apply')?.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
没有简单、直接的方法可以使用构造函数来完成此操作。这是因为当您使用 new 关键字调用构造函数时会发生特殊的事情,因此如果您不打算这样做,则必须模拟所有这些特殊的事情。它们是:
prototype
属性。constructor
属性。this
值调用构造函数(您正在这样做)。我认为就是这样,但值得在
因此,如果您可以避免它并直接使用构造函数,我会这样做。 :-) 如果你做不到,你仍然可以做到,只是很尴尬并且需要解决方法。 (另请参阅此相关答案 在 StackOverflow 上,尽管我在这里介绍了所有内容 [然后还介绍了一些]。)
您最大的问题是上面的#2:设置对象的内部原型。很长一段时间以来,没有标准的方法来做到这一点。某些浏览器支持执行此操作的 __proto__ 属性,因此您可以使用它(如果存在)。好消息是 ECMAScript 5 引入了一种明确执行此操作的方法:
Object.create
。所以像 Chrome 这样的尖端浏览器就会有这个功能。但是,如果您使用的浏览器既没有Object.create
也没有__proto__
,那就有点难看了:1) 定义一个自定义构造函数。
2) 将其
prototype
属性设置为真实构造函数的prototype
属性3) 使用它创建一个空白对象实例。
它会为您处理原型。然后继续:
4) 将该实例上的
constructor
属性替换为真正的构造函数。5) 通过
apply
调用真正的构造函数。6)如果真正的构造函数的返回值是一个对象,就用它来代替你创建的那个;否则,请使用您创建的那个。
像这样的东西(live example):
您可以更进一步,仅在必要时使用假构造函数,首先查看是否支持
Object.create
或__proto__
,如下所示 (实时示例):在 Chrome 6 上,上面使用
Object.create
;在 Firefox 3.6 和 Opera 上,它使用__proto__
。在 IE8 上,它使用伪造的构造函数。上面的内容相当即兴,但它主要解决了我在这方面意识到的问题。
There's no simple, straightforward way to do this with a constructor function. This is because special things happen when you use the
new
keyword to call a constructor function, and so if you're not going to do that, you have to emulate all of those special things. They are:prototype
property.constructor
property.this
value (you're doing that).I think that's about it, but worth double-checking in the spec.
So if you can avoid it and just use the constructor function directly, I'd do that. :-) If you can't, though, you can still do it, it's just awkward and involves workarounds. (See also this related answer here on StackOverflow, although I cover all of the ground here [and then some] as well.)
Your biggest issue is #2 above: Setting the internal prototype of the object. For a long time, there was no standard way to do this. Some browsers supported a
__proto__
property that did it, so you can use that if it's there. The good news is that ECMAScript 5 introduces a way to do this explicitly:Object.create
. So cutting-edge browsers like Chrome will have that. But if you're dealing with a browser that has neitherObject.create
nor__proto__
, it gets a bit ugly:1) Define a custom constructor function.
2) Set its
prototype
property to theprototype
property of the real constructor function3) Use it to create a blank object instance.
That handles the prototype for you. Then you continue with:
4) Replace the
constructor
property on that instance with the real constructor function.5) Call the real constructor function via
apply
.6) If the return value of the real constructor function is an object, use it instead of the one you created; otherwise, use the one you created.
Something like this (live example):
You could take it a step further and only use the fake constructor if necessary, looking to see if
Object.create
or__proto__
are supported first, like this (live example):On Chrome 6, the above uses
Object.create
; on Firefox 3.6 and Opera, it uses__proto__
. On IE8, it uses the fake constructor function.The above is fairly off-the-cuff, but it mostly handles the issues I'm aware of in this area.
来自 developer.mozilla:
绑定函数自动适合与 new 运算符一起使用来构造由目标函数创建的新实例。当使用绑定函数构造值时,所提供的 this 将被忽略。但是,提供的参数仍会添加到构造函数调用之前。
也就是说,我们仍然需要使用 apply 将参数从数组中取出并放入绑定调用中。此外,我们还需要将bind的函数重置为apply函数的this参数。这为我们提供了一个非常简洁的单行代码,完全可以满足需要。
From developer.mozilla:
Bound functions are automatically suitable for use with the new operator to construct new instances created by the target function. When a bound function is used to construct a value, the provided this is ignored. However, provided arguments are still prepended to the constructor call.
That said, we still need to use apply to get the arguments out of an array and into the bind call. Further, we need to also reset bind's function as the this argument of the apply function. This gives us a very succinct one-liner that does exactly as needed.
由于最初的问题相当老了,这里有一个几乎适用于任何现代浏览器的更简单的方法(截至撰写本文时,2018 年,我正在计算 Chrome、FF、IE11、Edge...)。
这两行还解释了 new 运算符的基本工作原理。
Since the original question is quite old, here's a simpler methods in practically any modern browser (as of writing, 2018, I'm counting Chrome, FF, IE11, Edge...).
Those two lines also explain how the
new
operator basically works.我认为实现此目的的另一种方法可能是扩展 Ext Template 类,以便新对象的构造函数接受您的数组并执行您想做的事情。这样,所有的构建工作都会为您完成,您只需使用参数调用超类的构造函数即可。
I think another way of achieving this could be to extend the Ext Template class so that the constructor of the new object takes your array and does the stuff you want to do. This way all the constructing stuff would be done for you and you could just call the constructor of your superclass with your arguments.