【问题标题】:Can I construct a JavaScript object without using the new keyword?我可以在不使用 new 关键字的情况下构造 JavaScript 对象吗?
【发布时间】:2010-12-25 17:32:59
【问题描述】:

这是我想做的:

function a() {
  // ...
}
function b() {
  //  Some magic, return a new object.
}
var c = b();

c instanceof b // -> true
c instanceof a // -> true
b instanceof a // -> true

有可能吗?我可以通过将a 挂接到它的原型链中轻松地使b 成为a 的一个实例,但是我必须这样做new b(),这是我试图避免的。我想要的可能吗?

更新:我认为明智地使用b.__proto__ = a.prototype 是可能的。下班后我会尝试更多。

更新 2: 以下是您能获得的最接近的内容,这对我来说已经足够了。感谢大家提供有趣的答案。

function a() {
  // ...
}
function b() {
  if (!(this instanceof arguments.callee)) {
    return new arguments.callee();
  }
}
b.__proto__ = a.prototype

var c = b();
c instanceof b // -> true
c instanceof a // -> false
b instanceof a // -> true

更新 3:我在 blog post on 'power constructors' 中找到了我想要的,一旦我添加了基本的 b.__proto__ = a.prototype 行:

var object = (function() {
     function F() {}
     return function(o) {
         F.prototype = o;
         return new F();
     };
})();

function a(proto) {
  var p = object(proto || a.prototype);
  return p;
}

function b(proto) {
  var g = object(a(proto || b.prototype));
  return g;
}
b.prototype = object(a.prototype);
b.__proto__ = a.prototype;

var c = b();
c instanceof b // -> true
c instanceof a // -> true
b instanceof a // -> true
a() instanceof a // -> true

【问题讨论】:

  • 我很好奇:你为什么要避免新的?
  • 我正在创建 Scala 案例类的 Javascript 版本:github.com/pr1001/caseclass.js
  • @pr1001:不明白为什么这需要您解决new 关键字。而__proto__ 不会帮助你跨浏览器。
  • 这都是语法糖,仅此而已。但是,我认为这仍然是一个有趣的挑战。

标签: javascript inheritance prototype constructor


【解决方案1】:

你可以使用这个模式:

function SomeConstructor(){
   if (!(this instanceof SomeConstructor)){
        return new SomeConstructor();
   }
   //the constructor properties and methods here
}

之后你可以这样做:

var myObj = SomeConstructor();

除了这个(相当旧的)答案:您可以使用module pattern 创建一个对象:

function Person(name, age, male) {
  name = name || 'unknown';
  age = age || 0;
  function get() {
    return ['This person is called ', name,
            (!male ? ', her' : ', his'),' age is ',
            age].join('');
  }
  function setAge(nwage) {
     age = nwage;
  }
  return Object.freeze({get: get, setAge: setAge});
}
// usage
var jane =  Person('Jane', 23)
   ,charles = Person('Charles', 32, 1)
   ,mary = Person('Mary', 16);

console.log(jane.get()); //=> This person is called Jane, her age is 23
mary.setAge(17);
console.log(mary.get()); //=> This person is called Mary, her age is 17

这是我使用该模式创建的一些 Date 功能的jsFiddle

【讨论】:

  • 我刚试过这个,看起来很有希望。以后会多开发的。
  • +1 这是允许构造函数在没有new 的情况下工作的惯用语。
  • 评论#1:但是……但是……但是……它仍然使用new。我没有看到这里的重点,它只是一种工厂方法,允许用户根据需要使用new。这只会导致团队中的一些成员使用new,而其他成员则不使用;维护的噩梦。支持其中一种(例如,如果作者没有使用new 调用构造函数,则通过包含您为生产删除的调试代码会引发异常),但不能同时使用两者。
  • 评论 #2:如果您想使用此模式,请确保使用命名函数(如您的示例中所示)并使用函数的名称,而不是 arguments.callee - 例如:if (!(this instanceof SomeConstructor)) .使用 arguments.callee显着地减慢你的函数(2-10x,取决于 JavaScript 引擎),并且会在 JavaScript 的新“严格”模式下抛出异常。
  • 抱歉,TJ,如果我不清楚,但我正在寻找 最终用户(而不是库创建者)避免使用 new 的代码。
【解决方案2】:

使用new 关键字有什么问题?

无论如何,听起来最好的办法是阅读 Javascript 继承: http://javascript.crockford.com/inheritance.html

【讨论】:

  • 无论如何,请阅读 Crockford。他知识渊博,受过教育。但不要将 Crockford 视为福音,尤其是因为他的一些早期文章(至少!)提倡极低效的编程实践。而且我没有立即看到 Crockford 关于在 JavaScript 中模拟经典继承的文章(现在被他视为“错误”)与 OP 发布的务实问题有何关联。
【解决方案3】:

有人在这个问题上发表了道格拉斯·克罗克福德的文章,它准确地解释了你的问题。

OO Javascript constructor pattern: neo-classical vs prototypal

【讨论】:

  • 真是一个.... 有趣的方法。如果它不完全做到这一点,那该死的,摆脱new(和this)。有点极端,但显示了语言的强大。
  • instanceof 不会返回问题中所需的结果。
【解决方案4】:

对您的具体问题的简单回答是:不。

这将帮助您确定为什么要避免使用new。也许其他答案之一提到的模式会有所帮助。但是,它们都不会导致 instanceof 在您的测试中返回 true。

新的操作本质上是:-

var x = (function(fn) { var r = {}; fn.call(r); return r;}(b);

但是不同之处在于构造 fn 使用某些内部属性附加到对象(是的,您可以使用 constructor 获取它,但设置它不会产生相同的效果)。让instanceof 按预期工作的唯一方法是使用new 关键字。

【讨论】:

    【解决方案5】:

    是的。如果您不想使用“new”关键字,只需使用原型,并在构造函数中返回一些内容。

    使用 'new' 只是告诉构造函数:

    1. 构造函数中的 'this' 关键字应该引用函数本身,而不是父对象(通常),如果此函数在全局范围内声明,父对象将是窗口对象。

    2. 对于新创建的对象/实例上的所有失败查找(未找到 obj 属性),请检查原始构造函数的原型属性。

    所以有了新的

        function Point(x, y) {
           this.x = x;
           this.y = y;
        }
        Point.prototype.getDistance = function(otherPoint){
           var Dx = (this.x - otherPoint.x) ** 2;
           var Dy = (this.y - otherPoint.y) ** 2;
           var d = Dx + Dy;
           d = Math.sqrt(d);
           return d
        }
        var pointA = new Point(3, 6);
        var pointB = new Point(5, 8);
        var distanceAB = pointA.getDistance(pointB);
    

    没有新的:

        function Point(x, y) {
           let d = Object.create(Point.prototype);
           d.x = x;
           d.y = y;
           return d
        }
        Point.prototype.getDistance = function(otherPoint){
           var Dx = (this.x - otherPoint.x) ** 2;
           var Dy = (this.y - otherPoint.y) ** 2;
           var d = Dx + Dy;
           d = Math.sqrt(d);
           return d
        }
        var pointA = Point(3, 6);
        var pointB = Point(5, 8);
        var distanceAB = pointA.getDistance(pointB);
    

    尝试在第一个示例中添加删除'new',你会看到'this'不再指代构造函数,而是指窗口对象。您将提供窗口 x 和 y 属性,这可能不是您想要的。

    【讨论】:

      【解决方案6】:

      如果您希望继承和 instanceof 工作,在一般情况下,您无法避免 new(根据 Crockford 文章 Zoidberg 间接链接到的极端情况),但是(再次)为什么要你想要或需要?

      我能想到你想避免它的唯一原因是,如果你试图将构造函数传递给另一段不知道它是构造函数的代码。在这种情况下,只需将其包装在工厂函数中即可:

      function b() {
          // ...
      }
      function makeB() {
          return new b();
      }
      var c = makeB();
      

      【讨论】:

        【解决方案7】:

        是的,你可以这样做。

        var User = function() {
          var privateMethod = function() {
            alert('hello');
          }
        
          return {
            sayHello : function() {
              privateMethod();
              this.done = true;
            }
          }
        }
        
        var user1 = User();
        

        这个方法有什么问题吗?

        【讨论】:

          【解决方案8】:

          您可以在没有 new 运算符的情况下创建实例(这是 Douglas Crockford http://yuiblog.com/blog/2006/11/13/javascript-we-hardly-new-ya/ 撰写的一篇很棒的文章)。但它不会帮助您处理“instanceof”的故事。

          【讨论】:

          • 这不是关于一般用途的new 使用,而是关于您可以避免它的特定位置,例如使用Object(改为使用{}),Array(改为使用[] ),尤其是Function.
          • 问题是,如果可能的话。文章解释说是的,它是可能的,它还涵盖了它为什么有用的一些方面。
          • @nemisj:不,它没有。它谈到不恰当地使用new(如new function() { ... }),not 是关于避免使用它。事实上,这篇文章根本没有谈论继承。
          • @T.J. Crowder:我的错误,没有注意到 c 实例 a 背后的想法 :)
          【解决方案9】:

          让 instanceof 工作的唯一方法是使用 new 关键字。 instanceof 利用 new 建立的____proto____。

          【讨论】:

          • 那么我可以对 proto 进行分配吗?
          • 当然,proto是“下划线下划线proto下划线下划线”。
          • 是的,愚蠢的格式。在 Rhino 和 Chrome 调试器中,__proto__ 似乎不是只读的。
          • 很高兴知道;我在某处读到____proto____ 已被弃用。考虑到不同浏览器采用的不同方法,可能很难实现。
          【解决方案10】:

          在 javascript 小书签中,您可以使用 eval(unescape("...")) 创建没有“new”运算符的对象:

          javascript:xhr=eval(unescape('new\x20XMLHttpRequest();'));alert(xhr);
          

          【讨论】:

            【解决方案11】:

            对于那些可能通过 Google 找到这个的人(比如我......),我进一步开发了原始解决方案以实现更好的通用用法:

            var myObject = function() {}; // hint: Chrome debugger will name the created items 'myObject'
            
            var object = (function(myself, parent) {  
                 return function(myself, parent) {
                     if(parent){
                         myself.prototype = parent;
                     }
                     myObject.prototype = myself.prototype;
                     return new myObject();
                 };
            })(); 
            
            a = function(arg) {
                 var me = object(a);
                 me.param = arg;
                 return me;
            };
            
            b = function(arg) {
                var parent = a(arg),
                    me = object(b, parent)
                ;
                return me;
            };
            
            var instance1 = a();
            var instance2 = b("hi there");
            
            console.log("---------------------------")
            console.log('instance1 instance of a: ' +  (instance1 instanceof a))
            console.log('instance2 instance of b: ' +  (instance2 instanceof b))
            console.log('instance2 instance of a: ' +  (instance2 instanceof a))
            console.log('a() instance of a: ' +  (a() instanceof a))
            console.log(instance1.param)
            console.log(instance2.param)

            【讨论】:

              【解决方案12】:

              通过使用Object.create 和经典的Function.prototype。正确设置原型链,您可以保留 instanceof 关键字的正常功能,而无需使用任何 new 关键字。

              function A() {
                  return Object.create(A.prototype);
              }
              
              function B() {
                  return Object.create(B.prototype);
              }
              
              B.prototype = Object.create(A.prototype);
              
              var c = B();
              
              assert(c instanceof A);
              assert(c instanceof B);
              

              【讨论】:

                【解决方案13】:

                是的。

                function _new(classConstructor, ...args) {
                  var obj = Object.create(classConstructor.prototype);
                  classConstructor.call(obj, ...args);
                  return obj;
                }
                
                function test_new() {
                  function TestClass(name, location) {
                    this._name = name;
                    this._location = location;
                    this.getName = function() {
                      return this._name;
                    }
                  }
                
                  TestClass.prototype.getLocation = function() {
                    return this._location;
                  }
                  TestClass.prototype.setName = function(newName) {
                    this._name = newName;
                  }
                
                  const a = new TestClass('anil', 'hyderabad');
                  const b = _new(TestClass, 'anil', 'hyderabad');
                
                  const assert = console.assert
                  assert(a instanceof TestClass)
                  assert(b instanceof TestClass)
                  assert(a.constructor.name === 'TestClass');
                  assert(b.constructor.name === 'TestClass');
                  assert(a.getName() === b.getName());
                  assert(a.getLocation() === b.getLocation());
                  a.setName('kumar');
                  b.setName('kumar');
                  assert(a.getName() === b.getName());
                  console.log('All is well')
                }
                test_new()

                参考:https://gist.github.com/aniltallam/af358095bd6b36fa5d3dd773971f5fb7

                【讨论】:

                  【解决方案14】:

                  新操作符的Polyfill:

                  function sampleClass() {
                      this.cname = "sample";
                  }
                  
                  sampleClass.prototype.getCname = function() {
                      return this.cname;
                  }
                  
                  const newPolyfill = function(fname) {
                      if (typeof fname !== "function")
                      throw Error("can't call new on non-functional input");
                  
                      let newFun = {};
                      newFun.__proto__ = Object.assign(fname.prototype);
                      fname.prototype.constructor();
                  
                      for (let key in fname.prototype) {
                          if (typeof fname.prototype[key] !== "function") {
                          newFun[key] = fname.prototype[key];
                          delete newFun.__proto__[key];
                          delete fname.__proto__[key];
                         }
                      }
                  
                      return newFun;
                  }
                  
                  let newObj = new sampleClass();
                  console.log("new obj", newObj);
                  console.log("new cname", newObj.getCname());//sample
                  let newPolyObj = newPolyfill(sampleClass);
                  console.log("new poly obj", newPolyObj);
                  console.log("newPly cname", newPolyObj.getCname());//sample
                  console.log(newPolyObj instanceof(sampleClass)) // true
                  

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-06-13
                    • 2015-01-21
                    • 2021-10-15
                    • 1970-01-01
                    相关资源
                    最近更新 更多