在Derived.prototype = new Base使用'new'关键字的原因是什么

时间:2012-09-26 00:17:48

标签: javascript constructor prototype

以下代码的作用如下:

WeatherWidget.prototype = new Widget;

其中Widget是构造函数,我想用新函数WeatherWidget扩展Widget'类'。

new 关键字在那里做什么?如果遗漏了什么会发生什么?

6 个答案:

答案 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.prototypeDummy继承,它没有自己的属性:

[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。脱离我的头脑,我记得的是:

  1. 创建了一个全新的对象
  2. Widget可以使用this关键字来引用该对象。
  3. 如果Widget没有返回任何内容,则会创建此新对象。
  4. 此对象将继承一些其他属性,这些属性将指示它是由Widget创建的,用于跟踪属性链。
  5. 如果没有new关键字,则会调用窗口小部件

    1. 如果处于严格模式,this将设置为undefined.
    2. 否则,this将引用全局对象。 (浏览器调用window。)
    3. 如果该函数未返回任何内容,则会返回undefined
    4. 参考: 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"

详细了解JavaScript原型here。 来自Pivotal Labs的article也非常好。

答案 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