【发布时间】:2012-09-17 13:24:24
【问题描述】:
下面的代码做了什么:
WeatherWidget.prototype = new Widget;
其中Widget 是一个构造函数,我想用一个新函数WeatherWidget 扩展小部件“类”。
new 关键字在那里有什么作用?如果省略它会发生什么?
【问题讨论】:
标签: javascript constructor prototype
下面的代码做了什么:
WeatherWidget.prototype = new Widget;
其中Widget 是一个构造函数,我想用一个新函数WeatherWidget 扩展小部件“类”。
new 关键字在那里有什么作用?如果省略它会发生什么?
【问题讨论】:
标签: javascript constructor prototype
WeatherWidget.prototype = new Widget;
new 关键字调用Widget 作为构造函数,并将返回值分配给prototype 属性。 (如果您省略new,除非您添加参数列表(),否则您不会调用Widget。但是,以这种方式调用Widget 可能是不可能的。它肯定有可能如果它不是严格模式代码并且实现符合 ECMAScript Ed. 5.x 那里,那么破坏全局命名空间,因为那时构造函数中的this 将引用 ECMAScript 的全局对象。)
但这种方法实际上来自really viralbad example in the old Netscape JavaScript 1.3 Guide(反映在 Oracle,原 Sun)。
这样,您的WeatherWidget 实例将全部继承自相同 Widget 实例。原型链将是:
[new WeatherWidget()] → [new Widget()] → [Widget.prototype] → …
这可能很有用,但大多数时候您不希望它发生。除非您希望所有 WeatherWidget 实例在它们之间共享它们从该 Widget 实例继承的 属性值,否则您不应该在此处执行此操作,并且仅 通过它,来自Widget.prototype。另一个问题是您需要以这种方式调用父构造函数,这可能不允许像您那样在没有参数的情况下调用,或者无法正确初始化。它当然与模拟基于类的继承(例如来自 Java)无关。
在这些基于原型的语言中实现基于类的继承的正确方法是(最初由Lasse Reichstein Nielsen in comp.lang.javascript in 2003, for cloning objects 设计):
function Dummy () {}
Dummy.prototype = Widget.prototype;
WeatherWidget.prototype = new Dummy();
WeatherWidget.prototype.constructor = WeatherWidget;
constructor 原型属性也应该被修复,这样您的WeatherWidget 实例w 将具有预期的w.constructor === WeatherWidget,而不是w.constructor === Widget。但是,请注意它是可枚举的。
这样,WeatherWidget 实例将通过原型链继承属性,但不会在它们之间共享属性值,因为它们从 Widget.prototype 到 Dummy 继承,而Dummy 没有自己的属性:
[new WeatherWidget()] → [new Dummy()] → [Widget.prototype] → …
在 ECMAScript Ed 的实现中。 5 及更高版本,您可以并且应该使用
WeatherWidget.prototype = Object.create(Widget.prototype, {
constructor: {value: WeatherWidget}
});
相反。这具有额外的优势,即生成的 constructor 属性为 not writable, enumerable, or configurable。
只有当你显式调用父构造函数时才会调用它,从WeatherWidget,例如
function WeatherWidget (…)
{
Widget.apply(this, arguments);
}
请参阅我的JSX:object.js 中的Function.prototype.extend(),了解如何概括这一点。使用该代码,它将变成
WeatherWidget.extend(Widget);
我的Function.prototype.extend() 带有一个可选的第二个参数,您可以使用它轻松地扩充WeatherWidget 实例的原型:
WeatherWidget.extend(Widget, {
foo: 42,
bar: "baz"
});
相当于
WeatherWidget.extend(Widget);
WeatherWidget.prototype.foo = 42;
WeatherWidget.prototype.bar = "baz";
不过,您仍然需要在子构造函数中显式调用父构造函数;该部分不能合理地自动化。但是我的Function.prototype.extend() 为Function 实例添加了一个_super 属性,这使它更容易:
function WeatherWidget (…)
{
WeatherWidget._super.apply(this, arguments);
}
其他人也实现了类似的扩展。
【讨论】:
Object.create :-)
Object.create() 是否值得在Function.prototype.extend() 的分支中使用。
WeatherWidget.prototype = new Dummy();,在我的行中,括号丢失了。这有什么不同?
new 时,注意到程序是如何变化的——不再调用构造函数了吗?使用参数列表,构造函数作为函数的调用可能仍然会失败,但至少它不会静默失败。
this或者使用严格模式。
根据一些奇怪的 Javascript 规则,new Widget 实际上调用了构造函数,而不是返回对构造函数的引用。这个问题实际上回答了var a = new Widget()和var a = Widget()之间的区别。
简单来说,new 关键字告诉 Javascript 调用函数 Widget 的规则集与常规函数调用不同。在我脑海中浮现,我记得的是:
Widget 可以使用 this 关键字来引用该对象。Widget 没有返回任何内容,则会创建这个新对象。Widget 创建的,用于跟踪属性链。如果没有 new 关键字,对小部件的调用会
this 将被设置为undefined.
this 将引用全局对象。 (浏览器调用window。)undefined。参考:
new keyword
【讨论】:
javascript constructor 将填写更多详细信息。
new 关键字就不会调用 - 因为 OP 省略了它们
WeatherWidget.prototype = new Widget;
确实创建了Widget 构造函数的新实例并将其用作WeatherWidget 的原型对象。使用new keyword 创建新对象,将其继承链设置为Widget.prototype,并在其上应用构造函数(您可以在其中设置单独的属性'n'方法,或创建私有范围的变量)。
如果没有 new 关键字,则将 Widget 函数分配给 prototype 属性 - 这没有任何意义。如果您添加可选括号(即Widget()),它将正常调用该函数,但不是作为新实例的构造函数,而是使用全局对象作为上下文。另请参阅reference for the this keyword。
请注意,您不应真正使用此代码。如前所述,它通过调用构造函数创建一个新实例。但目的只是创建一个继承自 Widgets 原型对象的 empty 对象,而不是实例化某些东西(这可能会造成一些伤害,具体取决于代码)。相反,您应该使用Object.create(或其popular shim):
WeatherWidget.prototype = Object.create(Widget.prototype);
另见Javascript basic inheritance vs Crockford prototypical inheritance
【讨论】:
constructor 属性。这就是 Object.create() 的“流行 shim”失败的地方——即使更新,必须失败——因为你无法模拟不可枚举性。以后只能hasOwnProperty()-过滤掉。这仍然留下了只写和不可配置,只能部分模拟(取决于实现)。
{constructor:{value:WeatherWidget}} 作为第二个参数传递,并且您可能需要使用不太流行的 shim :-) 但是,我不确定哪个代码取决于正确设置的 @987654343 @属性?
isInstance() 方法。比单独输入鸭子更精确。
用简单的英语来说,你是在扩展一个类和另一个类。原型只能是对象,因此您将WeatherWidget 的原型设置为Widget 的新实例。如果您删除了 new 关键字,您会将原型设置为不做任何事情的文字构造函数。
var Appendages = function(){
this.legs = 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = new Appendages;
var sara = new Features();
sara.legs;
// Returns 2.
了解原型可以是任何对象,这样的事情也可以:
var appendages = {
legs : 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = appendages;
var sara = new Features();
sara.legs;
// Returns 2.
在 JavaScript 中,如果在对象上找不到键,它会检查您扩展它的父对象。因此,您可以像这样动态更改父对象上的项目:
var appendages = {
legs : 2
};
var Features = function() {
this.ears = 4;
this.eyes = 1;
}
// Extend Features class with Appendages class.
Features.prototype = appendages;
var sara = new Features();
sara.legs;
// Returns 2.
appendages.hair = true;
sara.hair;
// Returns true.
请注意,这一切都发生在实例化过程中,这意味着您不能在创建对象后直接切换原型:
var foo = {name : 'bob'};
var bar = {nachos : 'cheese'};
foo.prototype = bar;
foo.nachos;
// undefined
但是,所有现代浏览器都带有这种较新的 __proto__ 方法,它允许您这样做:
var foo = {name : 'bob'};
var bar = {nachos : 'cheese'};
foo.__proto__ = bar;
foo.nachos
// "cheese"
阅读更多关于理解 JavaScript 原型的内容here。 这个来自 Pivotal Labs 的article 也很不错。
【讨论】:
Appendages 函数有什么用,为什么需要它的实例(包括原型对象)?
Appendages 最好是类构造函数而不是对象,以便您可以使用各种选项对其进行初始化。所以它可能是一个“引擎”类,您可以在其中传递引擎的大小,同时仍然使其可重用。
new 对于prototype 继承很重要;即
用方法创建构造函数
var Obj = function(){};
Obj.prototype = {};
Obj.prototype.foo = function(){console.log('foo');};
创建第二个构造函数来扩展第一个构造函数
var ExObj = function(){};
现在,如果我们在没有new 的情况下进行原型设计,
ExObj.prototype = Obj;
(new ExObj).foo(); // TypeError: Object #<Object> has no method 'foo'
这意味着我们没有继承Obj的原型,但是,如果我们使用new进行原型设计
ExObj.prototype = new Obj();
(new ExObj).foo(); // console logs 'foo'
此外,向ExObj 的原型添加新内容不会对其基础Obj 进行任何更改。
【讨论】:
JavaScript 函数是“MULTIPLE(2) PERSONALITIES”!!!
它们是带有输入和输出的常规函数,我们称之为function()。
当我们使用 new 关键字时,它们也是 JS 对象的构造函数。 >>>BUTprototype 属性对象的实例。
然后在WeatherWidget.prototype = 中,将要继承其属性的对象放入构造函数将创建的对象中,通常是new function() 而不是函数。
JavaScript 通过使用 instanceof 关键字命名构造函数创建的对象,在编程社区中造成巨大的混乱。> function f(){}undefined> new f() instanceof ftrue
【讨论】:
var o = Object.create(f.prototype); o.constructor(); f.prototype.isPrototypeOf(o) :-)