以下代码的作用如下:
WeatherWidget.prototype = new Widget;
其中Widget
是构造函数,我想用新函数WeatherWidget
扩展Widget'类'。
new
关键字在那里做什么?如果遗漏了什么会发生什么?
答案 0 :(得分:50)
WeatherWidget.prototype = new Widget;
new
关键字将Widget
作为构造函数调用,并将返回值分配给prototype
属性。 (如果省略new
,除非添加了参数列表Widget
,否则不会调用()
。但是,以这种方式调用Widget
可能是不可能的。肯定有可能破坏全局命名空间如果它不是严格模式代码并且实现符合ECMAScript Ed.5.x那里,因为构造函数中的this
会引用ECMAScript的全局对象。)
但这种方法实际上来自really viral bad 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
继承,它没有自己的属性:
[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);
}
另请参阅我的Function.prototype.extend()
中的JSX:object.js,了解如何概括此内容。使用该代码,它将成为
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()
向_super
实例添加了Function
属性,这样可以更轻松:
function WeatherWidget (…)
{
WeatherWidget._super.apply(this, arguments);
}
其他人已实施类似的扩展程序。
答案 1 :(得分:7)
根据一些奇怪的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
答案 2 :(得分:5)
WeatherWidget.prototype = new Widget;
确实创建了Widget
构造函数的新实例,并将其用作WeatherWidget
的原型对象。使用new
keyword创建新对象,将其继承链设置为Widget.prototype
,并在其上应用构造函数(您可以在其中设置单个属性的'n'方法,或创建私有 - 范围变量)。
如果没有new
关键字,那么Widget
函数就会被prototype
属性赋值 - 这没有任何意义。如果你要添加可选括号(即Widget()
),它将正常调用函数,但不能作为新实例的构造函数,而是将全局对象作为上下文。另请参阅reference for the this
keyword。
请注意,您不应该真正使用此代码。如上所述,它通过调用构造函数创建一个新实例。但目的只是创建一个继承自Widget
原型对象的空对象,而不是实例化某些东西(这可能会造成一些伤害,具体取决于代码)。相反,您应该使用Object.create
(或其popular shim):
WeatherWidget.prototype = Object.create(Widget.prototype);
另见Javascript basic inheritance vs Crockford prototypical inheritance
答案 3 :(得分:3)
用简单的英语,你将一个班级扩展到另一个班级。原型只能是一个对象,因此您将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"
答案 4 :(得分:2)
new
对原型继承非常重要;即
使用方法
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
。
答案 5 :(得分:0)
JavaScript函数是“多个(2)个性”!!!
它们是具有输入和输出的常规函数,我们称之为function()
。
当我们使用new
关键字时,它们也是JS对象的构造函数。 &GT;&GT;&GT; BUT&LT;&LT;&LT;新创建的对象不是构造函数的实例(就像基于类的继承中的类的对象)。新对象是构造函数的prototype
属性对象的实例。
然后在WeatherWidget.prototype =
中,您将要继承其属性的对象放置到构造函数将创建的对象中,通常为new function()
而不是函数。
JavaScript通过命名构造函数创建的对象,使用instanceof
关键字将它们的INSTANCES命名为编程社区,从而在编程社区中造成了巨大的混乱。
> function f(){}
undefined
> new f() instanceof f
true