JavaScriptでリストするのではなく、パラメーターの値の配列を使用してオブジェクトを構築するにはどうすればよいですか?
-
03-07-2019 - |
質問
これは可能ですか?さまざまなタイプのファクトリーを駆動する単一のベースファクトリー関数を作成しています(ただし、いくつかの類似点があります)。引数を配列としてベースファクトリーに渡すことができます。配列を介した関連クラスのコンストラクター。
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);
解決
これを試してください:
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);
他のヒント
注
-
コンストラクター関数は引数の存在に依存する可能性があるため、new myClass()
引数なしでは失敗する場合があります。
-
myClass.apply(something, args)
多くの場合、特にDateやNumberなどのネイティブクラスで呼び出された場合、失敗します。
「evalは悪」ということは知っていますが、この場合は次のことを試してみてください:
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;
}
そのように単純。
ハックはハックですが、おそらくこれは他のいくつかよりも少しエレガントです。呼び出し構文はあなたが望むものに似ていて、元のクラスをまったく修正する必要がないからです。
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"]);
だから、これらが誰かに役立つことを願っています-元のコンストラクタ関数をそのままにして、1行の簡単なコードで目的のものを取得できます(現在選択されているソリューション/回避策に必要な2行とは異なります) 。
フィードバックは大歓迎です。
UPDATE:注意すべきもう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() { /*...*/ };
上で提供した解決策は役に立つかもしれませんが、特定のニーズに最適な解決策を見つけるために何をしているのかを正確に理解する必要があり、何が起こっているのかを認識する必要があります私のソリューション「仕事」。私のソリューションがどのように機能するか理解できない場合は、時間をかけて解決してください。
代替ソリューション:他のオプションを見落とすものではありません。この猫の皮を剥ぐ方法は他にもあります(上記のアプローチと同様の注意事項があります)。これはもう少し難解です:
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;
};
お楽しみください
回避策はどうですか?
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);
1つの可能性は、コンストラクターを通常の関数呼び出しとして機能させることです。
function MyClass(arg1, arg2) {
if (!(this instanceof MyClass)) {
return new MyClass(arg1, arg2);
}
// normal constructor here
}
if
ステートメントの条件は、 MyClass
を通常の関数として呼び出した場合に真になります( call
/ applyを含む
this
引数が MyClassオブジェクト
)でない限り。
これらはすべて同等になりました:
new MyClass(arg1, arg2);
MyClass(arg1, arg2);
MyClass.call(null, arg1, arg2);
MyClass.apply(null, [arg1, arg2]);