![](/img/trans.png)
[英]Why use apply() or call() when you can pass the target object as a parameter?
[英]can you use call or apply with new?
与如何使用call或apply调用javascript构造函数有关?
但不尽相同,我尝试将SO答案应用于未正确调用时John Resig强制构造函数的问题 。
function User(first, last){
if ( !(this instanceof User) )
// the line I want to replace, or remove the redundancy from:
return new User(first, last);
this.name = first + " " + last;
}
var name = "Resig";
var user = User("John", name);
assert( user, "This was defined correctly, even if it was by mistake." );
assert( name == "Resig", "The right name was maintained." );
代码的目标行意味着,每次构造函数更改时,都必须记住要更改内部自调用参数。 在过去的三天内,我已经进行过3次有关此问题的项目旅行。
链接的问题中的所有示例都讨论传递constructor
,但是在这种情况下, constructor
是什么? 甚至还没有完成定义。
但到目前为止,所有尝试均未通过测试,也不会引发stackoverflow。
我如何确保调用构造函数时,即使没有使用new
关键字调用时,也能正确响应instanceof User
,同时又避免了参数参数的重复?
您可以使用Object.create
一些选项:
function User(first, last){
var rv;
if ( !(this instanceof User) ) {
// They called us without `new`: Create an object backed by `User.prototype`:
rv = Object.create(User.prototype);
// Now, call this function applying the arguments
User.apply(rv, arguments);
// Return the object
return rv;
}
// Normal constructor stuff
this.name = first + " " + last;
}
当然,不必为您创建的每个构造函数重复所有这些逻辑,可以使用一个辅助函数:
function constructWith(obj, ctor, args) {
if (obj instanceof ctor) {
return null;
}
obj = Object.create(ctor.prototype);
ctor.apply(obj, args);
return obj;
}
然后
function User(first, last){
var rv;
if ((rv = constructWith(this, User, arguments)) != null) {
return rv;
}
// Normal constructor stuff
this.name = first + " " + last;
}
this
了: function User(first, last){
var rv;
if (this instanceof User) {
// They (probably) used `new`, all is good, use `this`
rv = this;
} else {
// They didn't use `new`, create an object backed by `User.prototype`
rv = Object.create(User.prototype);
}
// ...use `rv`, not `this`, from here on
rv.name = first + " " + last;
// This is important for the case where they didn't use `new`, and harmless
// in the case where they did.
return rv;
}
如您所见,这要简单得多,但是如果您真的很喜欢语法高亮显示(严重的话,我有一个真正重要的客户,那么this
会跳出来),等等。
当然,您可以将其包装在帮助器中:
function useOrConstruct(obj, ctor) {
return obj instanceof ctor ? obj : Object.create(ctor.prototype);
}
然后
function User(first, last){
var rv = useOrConstruct(this, User);
// ...use `rv`, not `this`, from here on
rv.name = first + " " + last;
// This is important for the case where they didn't use `new`, and harmless
// in the case where they did.
return rv;
}
constructOMatic
当然,如果我们要定义助手,也许我们应该全力以赴:
function User() {
return constructOMatic(this, User, arguments, function(first, last) {
this.name = first + " " + last;
});
}
... constructOMatic
是:
function constructOMatic(obj, ctor, args, callback) {
var rv;
if (!(obj instanceof ctor)) {
obj = Object.create(ctor.prototype);
}
rv = callback.apply(obj, args);
return rv !== null && typeof rv === "object" ? rv : obj;
}
现在,您可以将this
用于回调中的内容。 在末尾的return
中摆弄rv
vs. obj
是为了模仿new
的行为( new
表达式的结果是由new
运算符创建的对象, 除非构造函数返回一个非null
对象引用,在这种情况下优先)。
Object.create
是在所有现代浏览器中都能找到的ES5功能,但是对于过时的浏览器,可以使用上面使用的ES.5的单参数版本:
if (!Object.create) {
Object.create = function(proto, props) {
if (typeof props !== "undefined") {
throw "The two-argument version of Object.create cannot be shimmed.";
}
function ctor() { }
ctor.prototype = proto;
return new ctor; // Yes, you really don't need () (but put them on if you prefer)
};
}
复制和粘贴非常简单,代码干净。 您无需更改它。
如果您接受eval
,则可以这样进行:
function User(first, last){
if ( !(this instanceof arguments.callee) ) {
var name = arguments.callee.name;
var param = [].map.call(arguments,function(e,i){return 'arguments['+i+']';});
return eval('new '+name+'('+ param +')');
}
this.name = first + " " + last;
}
//test
var user1 = User("John", "Resig");
var user2 = new User("John", "Resig");
没有eval
,您可以这样操作:
function instantiate(C,a){
switch(a.length){
case 0: return new C();
case 1: return new C(a[0]);
case 2: return new C(a[0],a[1]);
case 3: return new C(a[0],a[1],a[2]);
case 4: return new C(a[0],a[1],a[2],a[3]);
default : throw("too many arguments");
}
}
function User(first, last){
if ( !(this instanceof arguments.callee) ) {
return instantiate(arguments.callee, arguments);
}
this.name = first + " " + last;
}
//test
var user1 = User("John", "Resig");
var user2 = new User("John", "Resig");
在ECMAScript 6中,可以使用传播运算符将带有new关键字的构造函数应用于参数数组:
"use strict";
function User(first, last){
if ( !(this instanceof User) ) {
return new User(...arguments);
}
this.name = first + " " + last;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.