在Derived.prototype = new Base中使用“new”关键字的原因是什么?
下面的代码是做什么的:
WeatherWidget.prototype = new Widget;
其中Widget
是一个构造函数,我想用一个新的函数WeatherWidget
扩展Widget的“类”。
什么是new
关键字在那里做,如果被遗漏会发生什么?
WeatherWidget.prototype = new Widget;
new
关键字将Widget
作为构造函数调用,并将返回值分配给prototype
属性。 (如果你想省略new
,除非你添加了一个参数列表()
,否则你不会调用Widget
,但是这样调用Widget
可能是不可能的,如果它不是严格的模式,肯定有可能破坏全局的名字空间代码,并且实现符合ECMAScript Ed。5.x,因为那么在构造函数中这将引用ECMAScript的全局对象。)
但是这种方法实际上来自于旧版Netscape JavaScript 1.3指南 (甲骨文,以前称为Sun)中的一个病毒 坏例子 。
这样,您的WeatherWidget
实例将全部从相同的 Widget
实例inheritance。 原型链将是:
[new WeatherWidget()] → [new Widget()] → [Widget.prototype] → …
这可能是有用的,但大多数时候你不希望它发生。 除非您希望所有WeatherWidget
实例在其中共享从它们inheritance的Widget
实例的属性值 ,并且只能通过它从Widget.prototype
共享,否则不应在此处执行此Widget.prototype
。 另一个问题是你需要这样调用父构造函数,这样可能不允许在没有参数的情况下调用,或者不能正确初始化。 它与模拟基于类的inheritance无关,例如从Java中已知。
在这些基于原型的语言中实现基于类的inheritance的正确方法是(最初由Lasse Reichstein Nielsen在2003年的comp.lang.javascript
中为了克隆对象而devise ):
function Dummy () {} Dummy.prototype = Widget.prototype; WeatherWidget.prototype = new Dummy(); WeatherWidget.prototype.constructor = WeatherWidget;
( constructor
prototype属性也应该被修正,这样你的w.constructor === WeatherWidget
实例将会像预期的那样拥有w.constructor === WeatherWidget
,而不是w.constructor === Widget
。但是请注意,它之后是可枚举的)。
这样, WeatherWidget
实例将通过原型链inheritance属性,但不会共享属性值,因为它们通过Dummy
从Widget.prototype
inheritance,而Dummy
没有自己的属性:
[new WeatherWidget()] → [new Dummy()] → [Widget.prototype] → …
父构造函数只有在你明确地调用它的时候才会被调用,例如WeatherWidget
function WeatherWidget (…) { Widget.apply(this, arguments); }
(如果您处于一个非常有限的环境中,要求遵从ECMAScript Ed.5及更高版本 – 不要在Web上 – 或者ECMAScript Edition 5.1的实现已经无处不在 – 从现在起10年左右, 3那么长,你可以而且应该使用(Widget.prototype, {constructor: {value: WeatherWidget}});
WeatherWidget.prototype =
Object.create
(Widget.prototype, {constructor: {value: WeatherWidget}});
相反,它具有额外的优点,即得到的constructor
属性不可写,可枚举或可configuration的 。)
另请参阅我的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";
尽pipe如此,您仍然需要在子构造函数中显式调用父构造函数。 该部分不能合理地自动化。 但是,我的Function.prototype.extend()
将一个_super
属性添加到Function
实例中,使其更容易:
function WeatherWidget (…) { WeatherWidget._super.apply(this, arguments); }
其他人已经实施了类似的扩展。
根据一些奇怪的Javascript规则, new Widget
实际上调用了构造函数,而不是返回对构造函数的引用。 这个问题实际上回答了var a = new Widget()
和var a = Widget()
之间的区别。
简单地说, new
关键字告诉Javascript在不同于普通函数调用的一组规则下调用函数Widget
。 离开我的头顶,我记得的是:
- 有一个全新的对象创build
-
Widget
可以使用this
关键字来引用该对象。 - 如果
Widget
不返回任何东西,这个新的对象将被创build。 - 该对象将inheritance一些附加属性,这些属性将指示它是由
Widget
创build的,用于追踪属性链。
没有new
关键字,调用widget会
- 如果在严格模式下,
this
将被设置为undefined.
- 否则,
this
将引用全局对象。 (由浏览器调用window
。) - 如果函数没有返回任何东西,那么
undefined
将被返回。
参考: new
关键字
WeatherWidget.prototype = new Widget;
不会创buildWidget
构造函数的新实例,并将其用作WeatherWidget
的原型对象。 使用new
关键字创build新对象,将其inheritance链设置为Widget.prototype
,并在其上应用构造函数(您可以在其中设置各个属性的n方法或创build专用范围的variables)。
如果没有new
关键字,它将Widget
函数赋值给prototype
属性 – 这是没有任何意义的。 如果添加可选的括号(即Widget()
),它将正常调用该函数,但不是作为新实例的构造函数,而是以全局对象作为上下文。 另请参阅this
关键字的参考 。
注意你不应该使用这个代码。 如上所述,它通过调用构造函数来创build一个新的实例。 但目的只是创build一个从Widget
的原型对象inheritance的空对象,而不是实例化一些东西(这可能会造成一些伤害,取决于代码)。 相反,你应该使用Object.create
(或其stream行的垫片 ):
WeatherWidget.prototype = Object.create(Widget.prototype);
另请参阅Javascript基本inheritance与Crockford原型inheritance
用简单的英语,你正在扩大一个class级。 一个原型只能是一个对象,所以你把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.
请注意,这一切都发生在实例化过程中,这意味着您不能在创build对象之后切换原型:
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原型的信息 。 这篇来自Pivotal Labs的文章也非常好。
new
的原型inheritance是重要的; 即
用一个方法创build一个构造函数
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
的原型inheritance,但是,如果我们用new
原型
ExObj.prototype = new Obj(); (new ExObj).foo(); // console logs 'foo'
而且,向ExObj
的原型添加新的东西并不会对其基础Obj
进行任何更改。
JavaScript的function是“多(2)个人”!
它们是具有input和输出的正则函数,我们称之为function()
。
当我们使用new
关键字时,它们也是JS对象的构造函数。 >>> BUT <<<新创build的对象不是构造函数的实例(就像基于类的inheritance中的类对象)。 新对象是构造函数prototype
属性对象的实例。
然后在WeatherWidget.prototype =
你把你想要inheritance它的属性的对象的构造函数将创build的对象,这通常是new function()
而不是一个函数。
JavaScript在编程社区中造成了巨大的混乱,通过命名由构造函数创build的对象,使用instanceof
关键字指定它们的INSTANCES。
> function f(){}
undefined
> new f() instanceof f
true