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

Is there any difference between constructor function and prototype object when using inheritance?

本文关键字:对象 什么 区别 原型 继承 构造函数      更新时间:2023-09-26

考虑以下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,但有什么区别呢?我为什么要在不同的地方做这件事?

区别在于属性在原型链中的位置

假设我们有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实例相同的属性(继承)。

一个很大的区别是,如果你改变了原型的属性,这些改变将适用于所有的实例,包括那些已经存在的,而如果你改变了一个在构造函数中创建的属性,它只会在你改变它的实例中改变它。

关于在构造函数中设置bar导致每个实例都有自己的函数副本的其他一些答案:如果您在构造函数中有一个函数表达式,如问题中的代码所示,那么这是正确的,但如果您像这样分配函数引用,则不正确:

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

在这种情况下,所有实例将有一个bar属性引用相同的函数-但一个单独的实例仍然可以将其bar属性分配给其他东西,而不影响其他实例

添加到现有答案:

创建原型函数将允许继承对它的更改。例如,如果你写

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

我想我回答的问题是正确的,否则请告诉我。

区别在于使用prototype只会产生一个实例。

例如,baz。在一个例子中Bar和baz是一样的。酒吧在另一个。它们将在'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之间略有不同的地方。

使用原型将创建一个bar实例。所以a.barb.bar是一样的。如果你用另一种方法,它们将是两个不同的函数(做同样的事情)。

相关文章: