我可以在不使用 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 对象吗?的主要内容,如果未能解决你的问题,请参考以下文章
JSON.net:如何在不使用默认构造函数的情况下反序列化?