簡體   English   中英

如何使用參數的值數組構造對象,而不是在JavaScript中列出它們?

[英]How can I construct an object using an array of values for parameters, rather than listing them out, in JavaScript?

這可能嗎? 我正在創建一個基本工廠函數來驅動不同類型的工廠(但有一些相似之處),我希望能夠將參數作為數組傳遞給基礎工廠,然后可能創建一個填充參數的新對象的實例。通過數組的相關類的構造函數。

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

看起來好像無論如何使用apply創建一個對象的實例,是嗎?

像(這不起作用):

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) 

    在許多情況下會失敗,特別是在日期或數字等本機類上調用時。

我知道“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;
}

就那么簡單。

(它的作用與Instance.New這個博客文章中相同

黑客攻擊是黑客攻擊,但也許這個黑客比其他人更優雅,因為調用語法類似於你想要的,你根本不需要修改原始類:

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

因此,我希望這些對某些人有用 - 它們允許您單獨保留原始構造函數並通過一個簡單的代碼行獲得您所需的內容(與當前所選解決方案/解決方法所需的兩行不同)。

歡迎並感謝您的反饋。


更新:另外需要注意的一點 - 嘗試使用這些不同的方法創建相同類型的實例,然后檢查它們的構造函數屬性是否相同 - 如果您需要檢查類型,可能需要這樣做賓語。 我的意思最好用以下代碼說明:

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

嘗試與其他一些黑客,看看會發生什么。 理想情況下,您希望它們是相同的類型。


CAVEAT:正如評論中所指出的,這不適用於使用匿名函數語法創建的構造函數,即

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

一種可能性是使構造函數作為普通函數調用工作。

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

    // normal constructor here
}

ifMyClass作為普通函數調用,則if語句的條件將為true(包括call / apply ,只要this參數不是MyClass object )。

現在所有這些都是等價的:

new MyClass(arg1, arg2);
MyClass(arg1, arg2);
MyClass.call(null, arg1, arg2);
MyClass.apply(null, [arg1, arg2]);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM