Use of .apply() with 'new' operator. Is this possible?

后端 未结 30 2722
Happy的楠姐
Happy的楠姐 2020-11-22 00:39

In JavaScript, I want to create an object instance (via the new operator), but pass an arbitrary number of arguments to the constructor. Is this possible?

相关标签:
30条回答
  • 2020-11-22 01:08
    function createSomething() {
        var args = Array.prototype.concat.apply([null], arguments);
        return new (Function.prototype.bind.apply(Something, args));
    }
    

    If your target browser doesn't support ECMAScript 5 Function.prototype.bind, the code won't work. It is not very likely though, see compatibilty table.

    0 讨论(0)
  • 2020-11-22 01:09

    Matthew Crumley's solutions in CoffeeScript:

    construct = (constructor, args) ->
        F = -> constructor.apply this, args
        F.prototype = constructor.prototype
        new F
    

    or

    createSomething = (->
        F = (args) -> Something.apply this, args
        F.prototype = Something.prototype
        return -> new Something arguments
    )()
    
    0 讨论(0)
  • 2020-11-22 01:11

    Yes we can, javascript is more of prototype inheritance in nature.

    function Actor(name, age){
      this.name = name;
      this.age = age;
    }
    
    Actor.prototype.name = "unknown";
    Actor.prototype.age = "unknown";
    
    Actor.prototype.getName = function() {
        return this.name;
    };
    
    Actor.prototype.getAge = function() {
        return this.age;
    };
    

    when we create an object with "new" then our created object INHERITS getAge(), But if we used apply(...) or call(...) to call Actor, then we are passing an object for "this" but the object we pass WON'T inherit from Actor.prototype

    unless, we directly pass apply or call Actor.prototype but then.... "this" would point to "Actor.prototype" and this.name would write to: Actor.prototype.name. Thus affecting all other objects created with Actor...since we overwrite the prototype rather than the instance

    var rajini = new Actor('Rajinikanth', 31);
    console.log(rajini);
    console.log(rajini.getName());
    console.log(rajini.getAge());
    
    var kamal = new Actor('kamal', 18);
    console.log(kamal);
    console.log(kamal.getName());
    console.log(kamal.getAge());
    

    Let's try with apply

    var vijay = Actor.apply(null, ["pandaram", 33]);
    if (vijay === undefined) {
        console.log("Actor(....) didn't return anything 
               since we didn't call it with new");
    }
    
    var ajith = {};
    Actor.apply(ajith, ['ajith', 25]);
    console.log(ajith); //Object {name: "ajith", age: 25}
    try {
        ajith.getName();
    } catch (E) {
        console.log("Error since we didn't inherit ajith.prototype");
    }
    console.log(Actor.prototype.age); //Unknown
    console.log(Actor.prototype.name); //Unknown
    

    By passing Actor.prototype to Actor.call() as the first argument, when the Actor() function is ran, it executes this.name=name, Since "this" will point to Actor.prototype, this.name=name; means Actor.prototype.name=name;

    var simbhu = Actor.apply(Actor.prototype, ['simbhu', 28]);
    if (simbhu === undefined) {
        console.log("Still undefined since the function didn't return anything.");
    }
    console.log(Actor.prototype.age); //simbhu
    console.log(Actor.prototype.name); //28
    
    var copy = Actor.prototype;
    var dhanush = Actor.apply(copy, ["dhanush", 11]);
    console.log(dhanush);
    console.log("But now we've corrupted Parent.prototype in order to inherit");
    console.log(Actor.prototype.age); //11
    console.log(Actor.prototype.name); //dhanush
    

    Coming back to orginal question how to use new operator with apply, here is my take....

    Function.prototype.new = function(){
        var constructor = this;
        function fn() {return constructor.apply(this, args)}
        var args = Array.prototype.slice.call(arguments);
        fn.prototype = this.prototype;
        return new fn
    };
    
    var thalaivar = Actor.new.apply(Parent, ["Thalaivar", 30]);
    console.log(thalaivar);
    
    0 讨论(0)
  • 2020-11-22 01:16

    @Matthew I think it's better to fix the constructor property also.

    // Invoke new operator with arbitrary arguments
    // Holy Grail pattern
    function invoke(constructor, args) {
        var f;
        function F() {
            // constructor returns **this**
            return constructor.apply(this, args);
        }
        F.prototype = constructor.prototype;
        f = new F();
        f.constructor = constructor;
        return f;
    }
    
    0 讨论(0)
  • 2020-11-22 01:16

    Any function (even a constructor) can take a variable number of arguments. Each function has an "arguments" variable which can be cast to an array with [].slice.call(arguments).

    function Something(){
      this.options  = [].slice.call(arguments);
    
      this.toString = function (){
        return this.options.toString();
      };
    }
    
    var s = new Something(1, 2, 3, 4);
    console.log( 's.options === "1,2,3,4":', (s.options == '1,2,3,4') );
    
    var z = new Something(9, 10, 11);
    console.log( 'z.options === "9,10,11":', (z.options == '9,10,11') );
    

    The above tests produce the following output:

    s.options === "1,2,3,4": true
    z.options === "9,10,11": true
    
    0 讨论(0)
  • 2020-11-22 01:16

    A revised solution from @jordancpaul's answer.

    var applyCtor = function(ctor, args)
    {
        var instance = new ctor();
        ctor.prototype.constructor.apply(instance, args);
        return instance;
    }; 
    
    0 讨论(0)
提交回复
热议问题