我可以在不使用 new 关键字的情况下构造 JavaScript 对象吗?

Posted

技术标签:

【中文标题】我可以在不使用 new 关键字的情况下构造 JavaScript 对象吗?【英文标题】:Can I construct a JavaScript object without using the new keyword? 【发布时间】: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: How to create a new instance of a class without using the new keyword?的可能重复 【参考方案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 发布的务实问题有何关联。 ***.com/questions/377716/…【参考方案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' 只是告诉构造函数:

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

    对于新创建的对象/实例上的所有失败查找(未找到 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

【讨论】:

以上是关于我可以在不使用 new 关键字的情况下构造 JavaScript 对象吗?的主要内容,如果未能解决你的问题,请参考以下文章

C#中啥情况下要使用new关键字?这问题深奥额

ES6:调用类构造函数而不使用 new 关键字

JSON.net:如何在不使用默认构造函数的情况下反序列化?

在不调用构造函数的情况下创建 Vector 元素

如何在不将鉴别器传递给构造函数的情况下映射 Enumeration 类?

我应该如何在不使用 C++ 中的构造函数的情况下将值(不是指针)转换为子类?