为什么你不应该向JavaScript构造函数添加功能,而是通过原型

Why should you not add functionality to a JavaScript constructor but instead via prototype?

本文关键字:原型 功能 添加 不应该 JavaScript 构造函数 为什么      更新时间:2023-09-26

我看看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() );

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

原型对象上的函数(更一般地说,属性)由所有实例共享。无论创建多少个"Car"实例,单个"toString"函数都将保持为一个单独的对象。在构造函数中执行赋值时,将为每个函数对象创建一个新的函数对象。

显然,如果每个实例

都需要一个可能与其他实例不同的属性,则需要一个每个实例的属性。

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

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

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

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

如果在构造函数中定义函数,则每个实例都必须创建自己的函数版本,然后自己拥有它。这意味着如果您有 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实例,它使用新代码并以公里为单位显示距离。

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

原型上定义函数与在构造函数上定义函数也存在性能后果。当你在构造函数上定义一个函数时,每次使用构造函数时,你都会一次又一次地重新定义这个函数......

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

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

希望对您有所帮助!

每次

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

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

使用prototype方法,始终只有一个副本toString()