代码之家  ›  专栏  ›  技术社区  ›  fadedbee

如何使用call或apply调用javascript构造函数?[复制品]

  •  80
  • fadedbee  · 技术社区  · 15 年前

    这个问题已经有了答案:

    如何将下面的函数泛化为n个参数?(使用呼叫或应用?)

    有没有一种程序化的方法可以将参数应用于“new”?我不希望将构造函数视为普通函数。

    /**
     * This higher level function takes a constructor and arguments
     * and returns a function, which when called will return the 
     * lazily constructed value.
     * 
     * All the arguments, except the first are pased to the constructor.
     * 
     * @param {Function} constructor
     */ 
    
    function conthunktor(Constructor) {
        var args = Array.prototype.slice.call(arguments, 1);
        return function() {
            console.log(args);
            if (args.length === 0) {
                return new Constructor();
            }
            if (args.length === 1) {
                return new Constructor(args[0]);
            }
            if (args.length === 2) {
                return new Constructor(args[0], args[1]);
            }
            if (args.length === 3) {
                return new Constructor(args[0], args[1], args[2]);
            }
            throw("too many arguments");    
        }
    }
    

    QUnbe检验:

    test("conthunktorTest", function() {
        function MyConstructor(arg0, arg1) {
            this.arg0 = arg0;
            this.arg1 = arg1;
        }
        MyConstructor.prototype.toString = function() {
            return this.arg0 + " " + this.arg1;
        }
    
        var thunk = conthunktor(MyConstructor, "hello", "world");
        var my_object = thunk();
        deepEqual(my_object.toString(), "hello world");
    });
    
    7 回复  |  直到 8 年前
        1
  •  50
  •   James    15 年前

    试试这个:

    function conthunktor(Constructor) {
        var args = Array.prototype.slice.call(arguments, 1);
        return function() {
    
             var Temp = function(){}, // temporary constructor
                 inst, ret; // other vars
    
             // Give the Temp constructor the Constructor's prototype
             Temp.prototype = Constructor.prototype;
    
             // Create a new instance
             inst = new Temp;
    
             // Call the original Constructor with the temp
             // instance as its context (i.e. its 'this' value)
             ret = Constructor.apply(inst, args);
    
             // If an object has been returned then return it otherwise
             // return the original instance.
             // (consistent with behaviour of the new operator)
             return Object(ret) === ret ? ret : inst;
    
        }
    }
    
        2
  •  93
  •   kybernetikos    11 年前

    你就是这样做的:

    function applyToConstructor(constructor, argArray) {
        var args = [null].concat(argArray);
        var factoryFunction = constructor.bind.apply(constructor, args);
        return new factoryFunction();
    }
    
    var d = applyToConstructor(Date, [2008, 10, 8, 00, 16, 34, 254]);
    

    打电话稍微容易一点

    function callConstructor(constructor) {
        var factoryFunction = constructor.bind.apply(constructor, arguments);
        return new factoryFunction();
    }
    
    var d = callConstructor(Date, 2008, 10, 8, 00, 16, 34, 254);
    

    您可以使用以下任一项创建工厂功能:

    var dateFactory = applyToConstructor.bind(null, Date)
    var d = dateFactory([2008, 10, 8, 00, 16, 34, 254]);
    

    var dateFactory = callConstructor.bind(null, Date)
    var d = dateFactory(2008, 10, 8, 00, 16, 34, 254);
    

    它将与任何构造函数一起工作,而不仅仅是内置的或可以作为函数(如日期)双重使用的构造函数。

    但是它需要ecmascript 5.bind函数。垫片可能无法正常工作。

    另一种不同的方法,更多的是采用其他一些答案的风格,就是创建一个内置函数版本 new . 这不适用于所有内置设备(如日期)。

    function neu(constructor) {
        // http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2
        var instance = Object.create(constructor.prototype);
        var result = constructor.apply(instance, Array.prototype.slice.call(arguments, 1));
    
        // The ECMAScript language types are Undefined, Null, Boolean, String, Number, and Object.
        return (result !== null && typeof result === 'object') ? result : instance;
    }
    
    function Person(first, last) {this.first = first;this.last = last};
    Person.prototype.hi = function(){console.log(this.first, this.last);};
    
    var p = neu(Person, "Neo", "Anderson");
    

    现在,你当然可以 .apply .call .bind neu 正常情况下。

    例如:

    var personFactory = neu.bind(null, Person);
    var d = personFactory("Harry", "Potter");
    

    不过,我觉得我给出的第一个解决方案更好,因为它不依赖于您正确地复制内置的语义,并且它与内置正确地工作。

        3
  •  13
  •   Jason Orendorff Oliver    10 年前

    此函数与 new 在所有情况下。不过,它可能比999_墠墠墠墠墠墠墠墠墠墠墠墠墠墠

    function applyConstructor(ctor, args) {
        var a = [];
        for (var i = 0; i < args.length; i++)
            a[i] = 'args[' + i + ']';
        return eval('new ctor(' + a.join() + ')');
    }
    

    更新: 一旦ES6支持得到广泛应用,您就可以编写以下内容:

    function applyConstructor(ctor, args) {
        return new ctor(...args);
    }
    

    …但是您不需要,因为标准库功能 Reflect.construct() 做你想要的!

        4
  •  4
  •   Fuerteflojo    12 年前

    另一种方法,它要求修改正在调用的实际构造函数,但在我看来比使用eval()或在构造链中引入一个新的伪函数更简单…保持你的连续动作像

    function conthunktor(Constructor) {
      // Call the constructor
      return Constructor.apply(null, Array.prototype.slice.call(arguments, 1));
    }
    

    并修改正在调用的构造函数…

    function MyConstructor(a, b, c) {
      if(!(this instanceof MyConstructor)) {
        return new MyConstructor(a, b, c);
      }
      this.a = a;
      this.b = b;
      this.c = c;
      // The rest of your constructor...
    }
    

    所以你可以试试:

    var myInstance = conthunktor(MyConstructor, 1, 2, 3);
    
    var sum = myInstance.a + myInstance.b + myInstance.c; // sum is 6
    
        5
  •  3
  •   Louis    12 年前

    如果 Object.create 不可用。

    如果 创建对象 是可用的,那么使用它是更好的选择。 在node.js上,使用 创建对象 产生更快的代码。 下面是一个如何 创建对象 可以使用:

    function applyToConstructor(ctor, args) {
        var new_obj = Object.create(ctor.prototype);
        var ctor_ret = ctor.apply(new_obj, args);
    
        // Some constructors return a value; make sure to use it!
        return ctor_ret !== undefined ? ctor_ret: new_obj;
    }
    

    (显然, args 参数是要应用的参数列表。)

    我有一段最初使用的代码 eval 读取由另一个工具创建的一段数据。(是的, 埃瓦 邪恶。)这将实例化一棵由数百到数千个元素组成的树。基本上,JavaScript引擎负责分析和执行 new ...(...) 表达。我将系统转换为解析JSON结构,这意味着我必须让我的代码确定要为树中的每种类型的对象调用哪个构造函数。当我在我的测试套件中运行新代码时,我惊讶地看到相对于 埃瓦 版本。

    1. 测试套件 埃瓦 版本: 1秒。
    2. 使用临时构造函数的JSON版本的测试套件: 5秒。
    3. 带有JSON版本的测试套件,使用 创建对象 : 1秒。

    测试套件创建多个树。我计算了我的 applytoConstructor 函数在运行测试套件时被调用了大约125000次。

        6
  •  3
  •   Ghasem Kiani    11 年前

    在ecmascript 6中,可以使用spread运算符将带有new关键字的构造函数应用于参数数组:

    var dateFields = [2014, 09, 20, 19, 31, 59, 999];
    var date = new Date(...dateFields);
    console.log(date);  // Date 2014-10-20T15:01:59.999Z
    
        7
  •  1
  •   danyg    12 年前

    有一个可重复使用的解决方案。对于您希望使用apply或call方法调用的每个类,必须在转换为allowapply(“classnameinstring”)之前调用;该类必须位于同一个Scope或全局Scope中(例如,我不尝试发送ns.classname…)

    代码如下:

    function convertToAllowApply(kName){
        var n = '\n', t = '\t';
        var scrit = 
            'var oldKlass = ' + kName + ';' + n +
            kName + '.prototype.__Creates__ = oldKlass;' + n +
    
            kName + ' = function(){' + n +
                t + 'if(!(this instanceof ' + kName + ')){'+ n +
                    t + t + 'obj = new ' + kName + ';'+ n +
                    t + t + kName + '.prototype.__Creates__.apply(obj, arguments);'+ n +
                    t + t + 'return obj;' + n +
                t + '}' + n +
            '}' + n +
            kName + '.prototype = oldKlass.prototype;';
    
        var convert = new Function(scrit);
    
        convert();
    }
    
    // USE CASE:
    
    myKlass = function(){
        this.data = Array.prototype.slice.call(arguments,0);
        console.log('this: ', this);
    }
    
    myKlass.prototype.prop = 'myName is myKlass';
    myKlass.prototype.method = function(){
        console.log(this);
    }
    
    convertToAllowApply('myKlass');
    
    var t1 = myKlass.apply(null, [1,2,3]);
    console.log('t1 is: ', t1);
    
    推荐文章