11

これは可能ですか?さまざまなタイプのファクトリを駆動する単一の基本ファクトリ関数を作成しています (ただし、いくつかの類似点があります)。引数を配列として基本ファクトリに渡し、引数を入力する新しいオブジェクトのインスタンスを作成できるようにしたいと考えています。配列を介した関連クラスのコンストラクター。

JavaScript では、apply メソッドを使用して、配列を使用して複数の引数を持つ関数を呼び出すことができます。

namespace.myFunc = function(arg1, arg2) { //do something; }
var result = namespace.myFunc("arg1","arg2");
//this is the same as above:
var r = [ "arg1","arg2" ];
var result = myFunc.apply(namespace, r);

とにかく適用を使用してオブジェクトのインスタンスを作成する方法があるようには見えませんが、ありますか?

次のようなもの (これは機能しません):

var instance = new MyClass.apply(namespace, r);
4

5 に答える 5

15

これを試して:

var instance = {};
MyClass.apply( instance, r);

キーワード「new」は、新しいオブジェクトをコンストラクターに渡すだけで、コンストラクター関数内の this 変数になります。

コンストラクターの記述方法によっては、次のようにする必要がある場合があります。

var instance = {};
var returned = MyClass.apply( instance, args);
if( returned != null) {
    instance = returned;
}

更新: プロトタイプがある場合、これは機能しないというコメントがあります。これを試して。

function newApply(class, args) {
    function F() {
        return class.apply(this, args);
    }
    F.prototype = class.prototype;
    return new F();
}

newApply( MyClass, args);
于 2009-05-01T21:35:01.543 に答える
2

ご了承ください

  • new myClass()
    

    コンストラクター関数は引数の存在に依存する可能性があるため、引数がない場合は失敗する可能性があります。

  • myClass.apply(something, args)
    

    多くの場合、特に Date や Number などのネイティブ クラスで呼び出された場合は失敗します。

「評価は悪」であることは知っていますが、この場合、次のことを試してください。

function newApply(Cls, args) {
    var argsWrapper = [];
    for (var i = 0; i < args.length; i++) {
        argsWrapper.push('args[' + i + ']');
    }
    eval('var inst = new Cls(' + argsWrapper.join(',') + ');' );
    return inst;
}

そのような単純な。

(このブログ投稿と同じようInstance.Newに機能します)

于 2009-10-19T16:37:43.103 に答える
1

ハックはハックですが、呼び出し構文は必要なものに似ており、元のクラスをまったく変更する必要がないため、おそらくこれは他のいくつかよりも少しエレガントです。

Function.prototype.build = function(parameterArray) {
    var functionNameResults = (/function (.{1,})\(/).exec(this.toString());
    var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : "";
    var builtObject = null;
    if(constructorName != "") {
       var parameterNameValues = {}, parameterNames = [];
       for(var i = 0; i < parameterArray.length; i++) {
         var parameterName = ("p_" + i);
         parameterNameValues[parameterName] = parameterArray[i];
         parameterNames.push(("parameterNameValues." + parameterName));
       }
       builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues);
    }
    return builtObject;
};

これで、次のいずれかを実行してオブジェクトを作成できます。

var instance1 = MyClass.build(["arg1","arg2"]);
var instance2 = new MyClass("arg1","arg2");

確かに、Functionオブジェクトのプロトタイプを変更するのが嫌いな人もいるので、この方法で変更して、代わりに関数として使用できます。

function build(constructorFunction, parameterArray) {
    var functionNameResults = (/function (.{1,})\(/).exec(constructorFunction.toString());
    var constructorName = (functionNameResults && functionNameResults.length > 1) ? functionNameResults[1] : "";
    var builtObject = null;
    if(constructorName != "") {
       var parameterNameValues = {}, parameterNames = [];
       for(var i = 0; i < parameterArray.length; i++) {
         var parameterName = ("p_" + i);
         parameterNameValues[parameterName] = parameterArray[i];
         parameterNames.push(("parameterNameValues." + parameterName));
       }
       builtObject = (new Function("parameterNameValues", "return new " + constructorName + "(" + parameterNames.join(",") + ");"))(parameterNameValues);
    }
    return builtObject;
};

そして、あなたはそれをそのように呼ぶでしょう:

var instance1 = build(MyClass, ["arg1","arg2"]);

したがって、これらが誰かに役立つことを願っています-元のコンストラクター関数をそのままにして、(現在選択されているソリューション/回避策に必要な2行とは異なり)1行のコードで目的の結果を取得できます。

フィードバックは大歓迎です。


更新:もう1つ注意すべき点は、これらの異なるメソッドを使用して同じタイプのインスタンスを作成してから、コンストラクターのプロパティが同じかどうかを確認することです。物体。私が意味することは、次のコードで最もよく示されています。

function Person(firstName, lastName) {
   this.FirstName = firstName;
   this.LastName = lastName;
}

var p1 = new Person("John", "Doe");
var p2 = Person.build(["Sara", "Lee"]);

var areSameType = (p1.constructor == p2.constructor);

他のいくつかのハックでそれを試して、何が起こるかを見てください。理想的には、それらを同じタイプにする必要があります。


警告:コメントに記載されているように、これは無名関数構文を使用して作成されたコンストラクター関数では機能しません。

MyNamespace.SomeClass = function() { /*...*/ };

このように作成しない限り、次のようになります。

MyNamespace.SomeClass = function SomeClass() { /*...*/ };

上記で提供したソリューションは、役立つ場合と役に立たない場合があります。特定のニーズに最適なソリューションに到達するために何をしているのかを正確に理解する必要があります。また、私のソリューションを作成するために何が起こっているのかを認識する必要があります。」仕事。" 私のソリューションがどのように機能するか理解できない場合は、時間をかけて理解してください。


代替ソリューション:他のオプションを見落とすものではありません。これは、この猫の皮を剥ぐことができる他の方法の1つです(上記のアプローチと同様の注意事項があります)。これはもう少し難解です。

function partial(func/*, 0..n args */) {
   var args = Array.prototype.slice.call(arguments, 1);
   return function() {
      var allArguments = args.concat(Array.prototype.slice.call(arguments));
      return func.apply(this, allArguments);
   };
}

Function.prototype.build = function(args) {
   var constructor = this;
   for(var i = 0; i < args.length; i++) {
      constructor = partial(constructor, args[i]);
   }
   constructor.prototype = this.prototype;
   var builtObject = new constructor();
   builtObject.constructor = this;
   return builtObject;
};

楽しみ!

于 2009-05-02T01:58:31.940 に答える
0

回避策はどうですか?

function MyClass(arg1, arg2) {

    this.init = function(arg1, arg2){
        //if(arg1 and arg2 not null) do stuff with args
    }

    init(arg1, arg2);
}

では、次のことができます。

var obj = new MyClass();
obj.apply(obj, args);
于 2009-05-01T21:41:28.097 に答える
0

1 つの可能性は、コンストラクターを通常の関数呼び出しとして機能させることです。

function MyClass(arg1, arg2) {
    if (!(this instanceof MyClass)) {
        return new MyClass(arg1, arg2);
    }

    // normal constructor here
}

通常の関数として呼び出した場合 (引数が でない限り/を含む)、ifステートメントの条件はtrue になります。MyClasscallapplythisMyClass object

現在、これらはすべて同等です。

new MyClass(arg1, arg2);
MyClass(arg1, arg2);
MyClass.call(null, arg1, arg2);
MyClass.apply(null, [arg1, arg2]);
于 2009-05-01T21:55:06.333 に答える