【问题标题】:Programmatically create a new object以编程方式创建一个新对象
【发布时间】:2012-04-03 13:24:10
【问题描述】:

我正在尝试创建一个函数,该函数将接受参数arg1, arg2...,然后将它们传递给新对象 C 的构造函数,如下所示:new C(arg1, arg2...),因此要创建 C 的新实例,用户只需拥有打电话给C(arg) 而不是new C(arg)。这是我的第一次尝试:

var C = function(a){ this.a = a; }

var Cn = function(){
    new C.apply(this, arguments);
}

Cn(0)     // Should make a new C with a property a equal to 0
new C(0)  // ie the same as this

编辑:注意,我需要它接受任意数量的参数,而不是使用 eval。我正在创建一个在 js 中实现 Algebraic Data Types 的库。


编辑:解决方案是采用 Jeremy's Idea 并对其进行调整以采用无限数量的参数:

var C = function() {

    // A unique object so we can identify when we used the 'newless' constructor
    var newlessConstructorObj = {}

    // Check to see if C has been called with `new`
    if(!(this instanceof C))
        // If not pass arguments as single arg back to C
        return new C(newlessConstructorObj, arguments);


    // Check to see if we got here from the line above, if so the arguments were passed in the second arg
    var args = (arguments[0] === newlessConstructorObj) ? arguments[1] : arguments

    // Do stuff with args
    this.a = args[0];
}

C(0);
new C(0);

【问题讨论】:

    标签: javascript object prototype


    【解决方案1】:

    如果您希望能够使用或不使用 new 关键字来调用函数,则必须遵循以下模式:

    C = function(a) {
      if (!(this instanceof C)) {
        return new C(a);
      }
    
      this.a = a;
    }
    

    所以要创建一个新的“C”实例:

    c = new C(a);
    

    c = C(a);
    

    都将返回一个格式正确的 C 实例

    【讨论】:

    • +1 :这似乎是对单例模式的一个很好的改编......对吗?
    • 这看起来很不错并且解决了我的其他问题之一(C != Cn)。我会看看我是否可以改变我的代码来像这样工作。
    • 不错,但它不会传递参数。
    【解决方案2】:

    我会选择 Fabrizio Calderan 的解决方案,但因为您想要这个特定的功能 - 这是前辈告诉我们的:


    您可以将参数应用到 prototype.constructor(但在使用原生类型时会出现问题,例如 Number):

    var Cn = function(){
        return C.prototype.constructor.apply(C, arguments);
    }
    

    链接:Instantiating a JavaScript object by calling prototype.constructor.apply


    或者..使用eval:

    function construct(Constructor) 
    { 
     /* 
      * or Array.prototype.slice.call(arguments, 1).map(function() { ... }) 
      * in JavaScript 1.6+, compatibles, and with augmented Array.prototype 
      */ 
     var args = []; 
     for (var i = 1, len = arguments.length; i < len; i++) 
     { 
       args[i - 1] = "arguments[" + i + "]"; 
     } 
    
    /* or args.join(", ") if you need it pretty-printed */ 
     return eval("new Constructor(" + args + ")"); 
    } 
    
    function Foo() 
    { 
      window.alert(Array.prototype.slice.call(arguments, 0).join(", ")); 
    } 
    
    var f = construct(Foo, /bar/g, {baz: 42});
    

    链接:http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/ff1a104bdc33d5c8

    【讨论】:

    • 在我问这个问题之前,我一直在玩 prototype.constructor - 在 chrome 中 C.prototype.constructor.call(C, 0) 返回 undefined
    【解决方案3】:

    这样的?

    var C = function(obj){ 
      var a;
      for (a in obj) {
         this[a] = obj[a]; 
      }
    }
    var Cn = function(obj) { return new C(obj); }
    
    instance = Cn({
      a : 1,
      b : 2
    })  
    
    instance.a //1
    instance.b //2
    instance.c //undefined
    

    【讨论】:

    • 它有效,但我认为 OP 想要输入更少,而不是更多。考虑用数组替换对象(因为无论如何都不需要名称)。
    • 我只是扩展了返回对象实例并将所有参数分组为哈希表值的示例
    • 不幸的是,我无法更改用户传递的内容,因此我无法使用对象,但这激发了我的灵感,因为我可以更改“C”,因为用户从未看到它(只需将 Cn 中的 arguments 作为 arg 传递给 C - 我只在 C 中迭代 arguments)。
    【解决方案4】:

    使用固定数量的参数很简单:

    // assuming one arg
    function Cn(arg) {
      return new C(arg);
    }
    
    // assuming two args
    function Cn(arg0, arg1) {
      return new C(arg0, arg1);
    }
    

    等等。您甚至可以通过迭代 arguments 来创建一个字符串,然后 eval 它来为任意数量的参数创建一个通用版本。粗鲁,但有效。

    但是有什么意义呢?只是为了节省输入 4 个字符?

    【讨论】:

    • 啊,应该提到我需要它来处理一般数量的参数并且不使用eval。至于原因,我正在尝试为js做一个函数式编程库,我需要这个来实现Algebraic Data Types
    【解决方案5】:

    如果你不关心有一个正确的instanceof 检查你可以试试这个:

    var C = function(a){ this.a = a; }
    
    var Cn = function(){
        return C.apply({}, arguments); // notice an empty object here
    }
    
    Cn(0)     // Should make a new C with a property a equal to 0
    new C(0)  // ie the same as this
    

    【讨论】:

    • 未处理的错误:'C.apply' 不是构造函数
    • @c69: 抱歉,忘记换新返回
    【解决方案6】:

    为 ES6 更新,您可以使用扩展运算符:

    var C = function(a){ this.a = a; }
    
    var Cn = function(...args){
        return new C(...args);
    }
    
    assert.deepStrictEqual(Cn(10), new C(10));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-10
      • 2016-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-10
      相关资源
      最近更新 更多