50

以下代码有什么作用:

WeatherWidget.prototype = new Widget;

whereWidget是一个构造函数,我想用一个新函数来扩展 Widget 'class' WeatherWidget

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

4

6 回答 6

51
WeatherWidget.prototype = new Widget;

new关键字Widget作为构造函数调用,并将返回值分配给属性prototype。(如果省略,除非添加参数列表,否则new不会调用。但是,以这种方式调用可能是不可能的。如果它不是严格模式代码并且实现是,它肯定有可能破坏全局命名空间那里符合 ECMAScript Ed. 5.x,因为 then在构造函数中将引用 ECMAScript 的全局对象。)Widget()Widgetthis

但这种方法实际上来自旧的 Netscape JavaScript 1.3 指南(反映在 Oracle,以前的 Sun)中的一个非常 糟糕的例子。

这样,您的WeatherWidget实例都将继承自同一个 Widget实例。原型链将是:

[new WeatherWidget()] → [new Widget()] → [Widget.prototype] → …

这可能很有用,但大多数时候您不希望它发生。您不应该在此处这样做,除非您希望所有WeatherWidget实例在它们之间共享它们从该实例继承的属性值Widget,并且只能通过它,从Widget.prototype. 另一个问题是您需要以这种方式调用父构造函数,这可能不允许像您那样在没有参数的情况下调用,或者无法正确初始化。它当然与模拟基于类的继承毫无关系,例如从 Java 中。

在这些基于原型的语言中实现基于类的继承的正确方法是(最初由Lasse Reichstein Nielsencomp.lang.javascript在 2003 年设计,用于克隆对象):

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属性不可写、不可枚举或不可配置

仅当您显式调用父构造函数时才会调用它,WeatherWidget例如from

function WeatherWidget (…)
{
  Widget.apply(this, arguments);
}

另请参阅Function.prototype.extend()我的JSX:object.js以了解如何概括这一点。使用该代码,它将变成

WeatherWidget.extend(Widget);

MyFunction.prototype.extend()接受一个可选的第二个参数,您可以使用它轻松地扩充WeatherWidget实例的原型:

WeatherWidget.extend(Widget, {
  foo: 42,
  bar: "baz"
});

相当于

WeatherWidget.extend(Widget);
WeatherWidget.prototype.foo = 42;
WeatherWidget.prototype.bar = "baz";

但是,您仍然需要在子构造函数中显式调用父构造函数;该部分不能合理地自动化。但是我向实例Function.prototype.extend()添加了一个属性,这使得它更容易:_superFunction

function WeatherWidget (…)
{
  WeatherWidget._super.apply(this, arguments);
}

其他人已经实现了类似的扩展。

于 2012-09-26T01:15:55.723 回答
7

根据一些奇怪的 Javascript 规则,new Widget实际上是调用构造函数而不是返回对构造函数的引用。var a = new Widget()这个问题实际上回答了和之间的区别的问题var a = Widget()

简单来说,new关键字告诉 Javascript 在与Widget常规函数调用不同的规则集下调用函数。在我脑海中浮现,我记得的是:

  1. 创建了一个全新的对象
  2. Widget可以使用this关键字来引用该对象。
  3. 如果Widget不返回任何内容,则将创建此新对象。
  4. 该对象将继承一些额外的属性,这些属性表明它是由Widget用于跟踪属性链的创建的。

如果没有new关键字,对小部件的调用将

  1. 如果在严格模式下,this将设置为undefined.
  2. 否则,this将引用全局对象。window(由浏览器调用。)
  3. 如果函数没有返回任何东西,那么undefined将被返回。

参考: new关键词

于 2012-09-26T00:22:10.303 回答
5
WeatherWidget.prototype = new Widget;

确实创建了Widget构造函数的一个新实例并将其用作WeatherWidget的原型对象。使用new关键字创建新对象,将其继承链设置为Widget.prototype,并在其上应用构造函数(您可以在其中设置单独的属性'n'方法,或创建私有范围的变量)。

如果没有new关键字,它将是将Widget函数分配给prototype属性 - 这没有任何意义。如果您添加可选的括号(即Widget()),它将正常调用该函数,但不是作为新实例的构造函数,而是将全局对象作为上下文。另请参阅关键字的参考this

请注意,您不应该真正使用此代码。如前所述,它通过调用构造函数创建一个新实例。但目的只是创建一个从 s 原型对象继承的Widget对象,而不是实例化某些东西(这可能会造成一些伤害,具体取决于代码)。相反,您应该使用Object.create(或其流行的 shim):

WeatherWidget.prototype = Object.create(Widget.prototype);

另请参阅Javascript 基本继承与 Crockford 原型继承

于 2012-09-26T00:38:56.927 回答
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 原型的更多信息。Pivotal Labs 的这篇文章也很不错。

于 2012-09-26T00:40:30.343 回答
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.

于 2012-09-26T00:32:22.433 回答
0

JavaScript 函数是“MULTIPLE(2) PERSONALITIES”!!!

它们是具有输入和输出的常规函数​​,我们称之为function().

new当我们使用关键字时,它们也是 JS 对象的构造函数。>>>BUT<<< 新创建的对象不是构造函数的实例(如基于类的继承中的类对象)。新对象是prototype构造函数属性对象的实例。

然后WeatherWidget.prototype =将要继承其属性的对象放入构造函数将创建的对象中,这通常是new function()而不是函数。

instanceofJavaScript 通过使用关键字命名构造函数创建的对象,它们的实例,在编程社区中造成了巨大的混乱。
> function f(){}
undefined
> new f() instanceof f
true

于 2014-09-13T18:23:43.613 回答