86

以下の関数を一般化して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");    
    }
}

qUnitテスト:

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");
});
4

7 に答える 7

98

これはあなたがそれを行う方法です:

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

組み込み関数や関数としても機能するコンストラクター (Date など) だけでなく、任意のコンストラクターで動作します。

ただし、Ecmascript 5 .bind 関数が必要です。シムはおそらく正しく機能しません。

他のいくつかの回答のスタイルに近い別のアプローチは、組み込みの関数バージョンを作成することnewです。これはすべてのビルトイン (Date など) では機能しません。

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そしてもちろん、通常どおりor.callまたは.bindonを実行できますneu

例えば:

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

ただし、ビルトインのセマンティクスを正しく複製することに依存せず、ビルトインで正しく機能するため、私が提供する最初のソリューションの方が優れていると思います。

于 2013-01-17T11:51:09.310 に答える
50

これを試して:

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;

    }
}
于 2010-07-29T12:53:20.480 に答える
14

この関数は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()...しかし、標準ライブラリ関数が探していることを正確に実行するため、その必要はありません!

于 2011-12-12T17:10:52.500 に答える
5

ECMAScript 6 では、スプレッド演算子を使用して 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
于 2014-09-20T15:03:33.943 に答える
4

呼び出される実際のコンストラクターを変更する必要がある別のアプローチですが、 eval() を使用したり、構築チェーンに新しいダミー関数を導入したりするよりもきれいに思えます... conthunktor関数を次のように保ちます

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
于 2013-02-04T15:17:55.427 に答える