简体   繁体   English

您可以使用通话还是通过新电话申请?

[英]can you use call or apply with new?

related to How can I call a javascript constructor using call or apply? 如何使用call或apply调用javascript构造函数有关?

but not the same, I'm trying to apply the SO answers to John Resig's forcing a constructor when not called properly. 但不尽相同,我尝试将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." );

The target line of code means every time the constructor changes, someone has to remember to change the internal self-call arguments. 代码的目标行意味着,每次构造函数更改时,都必须记住要更改内部自调用参数。 I've already had a project trip over this issue 3 times in the last 3 days. 在过去的三天内,我已经进行过3次有关此问题的项目旅行。

All the examples in the linked question talk about passing the constructor , but what is the constructor in this case? 链接的问题中的所有示例都讨论传递constructor ,但是在这种情况下, constructor是什么? It's not even finished being defined yet. 甚至还没有完成定义。

but so far all attempts do not pass the test, or throw a stackoverflow. 但到目前为止,所有尝试均未通过测试,也不会引发stackoverflow。

How do I make sure the constructor being called results in something that responds properly to instanceof User even when called without the new keyword, while eliminating the repetition of argument parameters? 我如何确保调用构造函数时,即使没有使用new关键字调用时,也能正确响应instanceof User ,同时又避免了参数参数的重复?

Some options for you, all using Object.create : 您可以使用Object.create一些选项:

Option 1: 选项1:

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; 
} 

Of course, all of that logic doesn't have to be repeated for every constructor function you create, you can use a helper function: 当然,不必为您创建的每个构造函数重复所有这些逻辑,可以使用一个辅助函数:

function constructWith(obj, ctor, args) {
    if (obj instanceof ctor) {
        return null;
    }
    obj = Object.create(ctor.prototype);
    ctor.apply(obj, args);
    return obj;
}

then 然后

function User(first, last){ 
  var rv;

  if ((rv = constructWith(this, User, arguments)) != null) {
      return rv;
  }

  // Normal constructor stuff    
  this.name = first + " " + last; 
} 

Option 2: Don't use this much: 选项2:不要用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;
} 

As you can see, this is a lot simpler, but if you really like your syntax highlighting (seriously, I have a client to whom it really matters that this jumps out), etc... 如您所见,这要简单得多,但是如果您真的很喜欢语法高亮显示(严重的话,我有一个真正重要的客户,那么this会跳出来),等等。

And of course, you can wrap that up in a helper: 当然,您可以将其包装在帮助器中:

function useOrConstruct(obj, ctor) {
    return obj instanceof ctor ? obj : Object.create(ctor.prototype);
}

Then 然后

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;
} 

Option 3: constructOMatic 选择3: constructOMatic

Of course, if we're going to define helpers, maybe we should go whole-hog: 当然,如果我们要定义助手,也许我们应该全力以赴:

function User() {
    return constructOMatic(this, User, arguments, function(first, last) {
        this.name = first + " " + last;
    });
}

...where constructOMatic is: ... 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;
}

Now, you can use this to your heart's content within the callback. 现在,您可以将this用于回调中的内容。 That fiddling with rv vs. obj in the return at the end is to emulate the behavior of new (the result of a new expression is the object created by the new operator unless the constructor function returns a non- null object reference, in which case that takes precedence). 在末尾的return中摆弄rv vs. obj是为了模仿new的行为( new表达式的结果是由new运算符创建的对象, 除非构造函数返回一个非null对象引用,在这种情况下优先)。


Object.create is an ES5 feature found on all modern browsers, but the single-argument version of it used above can be shimmed for out-of-date browsers: 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)
    };
}

Copy and paste are very easy and the code is clean. 复制和粘贴非常简单,代码干净。 You need not to change it. 您无需更改它。

If you accept eval , you can do it like this: 如果您接受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");

Without eval , you can do it like this: 没有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");

In ECMAScript 6, you can use the spread operator to apply a constructor with the new keyword to an array of arguments: 在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM