使用继承时,构造函数和原型对象之间有什么区别吗?

时间:2011-10-20 09:46:34

标签: javascript

请考虑以下JavaScript代码段:

function foo() {
  this.bar = function() { };
}

// or... (if we used an empty constructor function)

foo.prototype.bar = function() { };

当我这样做时有什么不同:

function baz() {
}

baz.prototype = new foo();

在这两种情况下baz最终都会有成员bar,但有什么区别?为什么我会在不同的地方这样做?

4 个答案:

答案 0 :(得分:10)

不同之处在于属性所在的原型链中的 where

假设我们有f = new foo();b = new baz()。然后我们有以下情况:

foo的定义,不使用原型:

+-----------+     +---------------+
|     f     |     | foo.prototype |
| __proto__-+---->| constructor   |  
| bar       |     |               |
+-----------+     +---------------+

bar是对象本身的属性(f.howOwnProperty('bar')返回true)。

如果您将属性分配给原型,则情况为:

+-----------+     +---------------+
|     f     |     | foo.prototype |
| __proto__-+---->| constructor   |  
|           |     | bar           |
+-----------+     +---------------+

f没有自己的属性bar,但该属性与所有其他foo个实例共享

类似于第二个片段,其结果是

+-----------+     +---------------+     +---------------+
|     b     |     | foo instance  |     | foo.prototype |
| __proto__-+---->| __proto__    -+---->| constructor   |  
|           |     | bar           |     |               |
+-----------+     +---------------+     +---------------+

+-----------+     +---------------+     +---------------+
|     b     |     | foo instance  |     | foo.prototype |
| __proto__-+---->| __proto__    -+---->| constructor   |  
|           |     |               |     | bar           |
+-----------+     +---------------+     +---------------+

您为什么要这样做?

主要是关于结构而不是浪费记忆。您可以在构造函数中向对象添加函数:

function Foo() {
    this.bar = function() {};
}

但这也意味着Foo的每个实例都有自己的功能,即f1.bar === f2.barfalse,尽管两个函数的功能完全相同。

使用原型为您提供了一种清晰的方法来分隔所有实例和特定于实例的属性。

最后,它是“只是”继承这是软件开发中的一个概念(如聚合),可以在任何有意义的地方使用。您的第二个代码段基本上意味着baz 是-a foo,因此baz实例除了拥有自己的属性外,还具有与foo实例(继承)。

答案 1 :(得分:1)

一个很大的区别是,如果更改原型的属性,那些更改将应用​​于所有实例,包括那些已经存在的实例,而如果更改​​在构造函数中创建的属性,它将仅为实例更改它改变它。

关于在构造函数中设置bar的其他一些答案所说的,导致每个实例都有自己的函数副本:如果你在构造函数中有一个函数表达式,那就是真的,如下面的代码所示问题,但如果你指定一个像这样的函数引用,则不是这样的:

function myFunction() {}

function foo() {
   this.bar = myFunction;
}

在这种情况下,所有实例都将具有引用相同函数的bar属性 - 但是单个实例仍可以将其bar属性分配给其他内容,而不会影响其他实例。

答案 2 :(得分:0)

添加现有答案:

制作原型函数将允许对其进行更改。 即如果你写

function foo(){}
foo.prototype.bar = function(){return 1};
function baz(){}
baz.prototype = new foo();
new baz().bar(); //returns 1
foo.prototype.bar = function(){return 2};
new baz().bar(); //returns 2

但是,将它放在构造函数中会让从其继承的其他对象“拥有”该函数,但该函数不会被继承。

function foo(){this.bar = function(){return 1};}
function baz(){}
baz.prototype = new foo();
new baz().bar(); //returns 1
foo.prototype.bar = function(){return 2};
new baz().bar(); //returns 1

答案 3 :(得分:-1)

我想我在这里回答了正确的问题,否则请告诉我。

不同之处在于仅使用原型结果只有1个实例。

因此,例如,一个实例中的baz.bar与另一个实例中的baz.bar相同。他们将在'foo'实例中共享值:

function foo() {
    var x = 0;
    this.bar = function() {};

    this.getVal = function() {
        return x;
    }

    this.setVal = function(val) {
        x = val;
    }
}

function baz() {}

baz.prototype = new foo();

var a = new baz(),
    b = new baz();

a.setVal("1234")
console.log(b.getVal()); // prints '1234'

http://jsfiddle.net/jonathon/7GtRD/

如果ab直接调用'foo',则他们不会在foo内共享值。但是,这是设置this.bar和使用原型创建条形图之间略有不同的地方。

使用原型会创建一个bar实例。因此a.barb.bar相同。如果你以另一种方式做,它们将是两个不同的功能(做同样的事情)。

相关问题