5

我看一下 Addy Osmani 关于构造函数模式的章节:http: //addyosmani.com/resources/essentialjsdesignpatterns/book/#constructorpatternjavascript 我遇到了以下内容:

function Car( model, year, miles ) {

  this.model = model;
  this.year = year;
  this.miles = miles;

  this.toString = function () {
    return this.model + " has done " + this.miles + " miles";
  };
}

// Usage:

// We can create new instances of the car
var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );

// and then open our browser console to view the 
// output of the toString() method being called on 
// these objects
console.log( civic.toString() );
console.log( mondeo.toString() );

他说这对于 this.toString 函数来说并不是一件好事,因为它不是非常理想的并且不是在汽车类型的所有实例之间共享。但他没有解释这究竟意味着什么以及为什么这是一件坏事。他建议执行以下操作:

function Car( model, year, miles ) {

  this.model = model;
  this.year = year;
  this.miles = miles;

}


// Note here that we are using Object.prototype.newMethod rather than 
// Object.prototype so as to avoid redefining the prototype object
Car.prototype.toString = function () {
  return this.model + " has done " + this.miles + " miles";
};

// Usage:

var civic = new Car( "Honda Civic", 2009, 20000 );
var mondeo = new Car( "Ford Mondeo", 2010, 5000 );

console.log( civic.toString() );
console.log( mondeo.toString() );

有人可以解释为什么使用原型对象添加此功能是最佳/更好的吗?

4

5 回答 5

6

原型对象上的函数(嗯,更一般地说,属性)由所有实例共享。无论创建了多少个“Car”实例,这个单一的“toString”函数都将只是一个单独的对象。当您在构造函数中进行赋值时,会为每个函数对象创建一个新的函数对象。

显然,如果每个实例都需要一个可能与其他实例不同的属性,那么您需要一个每个实例的属性。

于 2013-09-25T13:35:16.643 回答
2

原型上的函数在每个实例之间共享。这意味着对于每个实例,这是完全相同的功能,而不是完全相同的副本。

如果您在构造函数中定义函数,则每个实例都必须创建自己的函数版本,然后自己拥有它。这意味着如果您有 100 个实例,那么您在内存中就有 100 个函数副本。这也意味着更新函数一点也不好玩,你必须在每个实例上都这样做。

如果函数在原型中,在原型中更新它意味着每个实例现在都使用更新后的代码(因为它们都使用相同的共享函数)

例如:

function Car( model, year, miles ) {

  this.model = model;
  this.year = year;
  this.miles = miles;

}

Car.prototype.toString = function () {
  return this.model + " has done " + this.miles + " miles";
};


var civic = new Car( "Honda Civic", 2009, 20000 );

console.log( civic.toString() );

Car.prototype.toString = function () {
  return this.model + " has done " + (this.miles * 1.60934) + " kilometers";
};


console.log( civic.toString() );

请注意我不需要更改civic实例,它使用新代码并以公里为单位显示距离。

于 2013-09-25T13:38:21.253 回答
2

对我来说,一个典型的经验法则是在构造函数中设置特定于给定实例的属性,例如名称、颜色等,并定义“类”应该作为原型上的函数共享的函数。

在原型和构造函数上定义函数也有性能影响。当您在构造函数上定义函数时,每次使用构造函数时,您都会一次又一次地重新定义函数......

http://jsperf.com/prototype-vs-instance-functions

您不希望name在原型上使用类似的东西,因为如果您进入并更改了Car.prototype.name,那么您将更改 . 的所有新实例的名称Car

希望有帮助!

于 2013-09-25T14:08:54.677 回答
2

当函数在构造函数中时,它将为“类”的每个新实例创建。因此,您创建的每个Car对象都会有自己的toString函数占用额外的内存。

如果toString通过原型添加,它只会存在一次,并且Car您创建的每个对象都将使用相同的功能。

它还整理了构造函数代码以仅包含与创建对象相关的内容。

于 2013-09-25T13:35:59.433 回答
1

每次你运行new CarJS 解析器都会在构造函数中执行代码,这意味着你正在创建另一个

this.toString = function () {
    return this.model + " has done " + this.miles + " miles";
  };

使用prototype方法总是只有一份toString().

于 2013-09-25T13:35:57.973 回答