继承就是一个对象可以访问另外一个对象中的属性和方法。不同的语言实现继承的方式是不同的,其中最典型的两种方式是基于类的设计基于原型继承的设计

JavaScript 仅仅在对象中引入了一个原型的属性,就实现了语言的继承机制,基于原型的继承省去了很多基于类继承时的繁文缛节,简洁而优美。

原型继承是如何实现的?

原型链 - 图1

原型链 - 图2
原型链 - 图3
原型链 - 图4

还有一点我们要注意,通常隐藏属性是不能使用 JavaScript 来直接与之交互的。虽然现代浏览器都开了一个口子,让 JavaScript 可以访问隐藏属性 proto,但是在实际项目中,我们不应该直接通过 proto 来访问或者修改该属性,其主要原因有两个:

  • 首先,这是隐藏属性,并不是标准定义的 ;
  • 其次,使用该属性会造成严重的性能问题。


构造函数是怎么创建对象的?


  1. function DogFactory(type,color){
  2. this.type = type
  3. this.color = color
  4. }
  5. var dog = new DogFactory('Dog','Black')

通过这种方式,我们就把后面的函数称为构造函数,因为通过执行 new 配合一个函数,JavaScript 虚拟机便会返回一个对象。如果你没有详细研究过这个问题,很可能对这种操作感到迷惑,为什么通过 new 关键字配合一个函数,就会返回一个对象呢?

其实当 V8 执行上面这段代码时,V8 会在背后悄悄地做了以下几件事情,模拟代码如下所示:

  1. var dog = {}
  2. dog.__proto__ = DogFactory.prototype
  3. DogFactory.call(dog,'Dog','Black')

原型链 - 图5

构造函数怎么实现继承?


  1. function DogFactory(type,color){
  2. this.type = type
  3. this.color = color
  4. //Mammalia
  5. //恒温
  6. this.constant_temperature = 1
  7. }
  8. var dog1 = new DogFactory('Dog','Black')
  9. var dog2 = new DogFactory('Dog','Black')
  10. var dog3 = new DogFactory('Dog','Black')

原型链 - 图6

从图中可以看出来,对象 dog1 到 dog3 中的 constant_temperature 属性都占用了一块空间,但是这是一个通用的属性,表示所有的 dog 对象都是恒温动物,所以没有必要在每个对象中都为该属性分配一块空间,我们可以将该属性设置公用的。

一个函数有以下几个隐藏属性:原型链 - 图7
每个函数对象中都有一个公开的 prototype 属性,当你将这个函数作为构造函数来创建一个新的对象时,新创建对象的原型对象就指向了该函数的 prototype 属性。当然了,如果你只是正常调用该函数,那么 prototype 属性将不起作用。

现在我们知道了新对象的原型对象指向了构造函数的 prototype 属性,当你通过一个构造函数创建多个对象的时候,这几个对象的原型都指向了该函数的 prototype 属性,如下图所示:

原型链 - 图8

这时候我们可以将 constant_temperature 属性添加到 DogFactory 的 prototype 属性上,代码如下所示:

  1. function DogFactory(type,color){
  2. this.type = type
  3. this.color = color
  4. //Mammalia
  5. }
  6. DogFactory. prototype.constant_temperature = 1
  7. var dog1 = new DogFactory('Dog','Black')
  8. var dog2 = new DogFactory('Dog','Black')
  9. var dog3 = new DogFactory('Dog','Black')