继承就是一个对象可以访问另外一个对象中的属性和方法。不同的语言实现继承的方式是不同的,其中最典型的两种方式是基于类的设计和基于原型继承的设计。
JavaScript 仅仅在对象中引入了一个原型的属性,就实现了语言的继承机制,基于原型的继承省去了很多基于类继承时的繁文缛节,简洁而优美。
原型继承是如何实现的?
还有一点我们要注意,通常隐藏属性是不能使用 JavaScript 来直接与之交互的。虽然现代浏览器都开了一个口子,让 JavaScript 可以访问隐藏属性 proto,但是在实际项目中,我们不应该直接通过 proto 来访问或者修改该属性,其主要原因有两个:
- 首先,这是隐藏属性,并不是标准定义的 ;
- 其次,使用该属性会造成严重的性能问题。
构造函数是怎么创建对象的?
function DogFactory(type,color){
this.type = type
this.color = color
}
var dog = new DogFactory('Dog','Black')
通过这种方式,我们就把后面的函数称为构造函数,因为通过执行 new 配合一个函数,JavaScript 虚拟机便会返回一个对象。如果你没有详细研究过这个问题,很可能对这种操作感到迷惑,为什么通过 new 关键字配合一个函数,就会返回一个对象呢?
其实当 V8 执行上面这段代码时,V8 会在背后悄悄地做了以下几件事情,模拟代码如下所示:
var dog = {}
dog.__proto__ = DogFactory.prototype
DogFactory.call(dog,'Dog','Black')
构造函数怎么实现继承?
function DogFactory(type,color){
this.type = type
this.color = color
//Mammalia
//恒温
this.constant_temperature = 1
}
var dog1 = new DogFactory('Dog','Black')
var dog2 = new DogFactory('Dog','Black')
var dog3 = new DogFactory('Dog','Black')
从图中可以看出来,对象 dog1 到 dog3 中的 constant_temperature 属性都占用了一块空间,但是这是一个通用的属性,表示所有的 dog 对象都是恒温动物,所以没有必要在每个对象中都为该属性分配一块空间,我们可以将该属性设置公用的。
一个函数有以下几个隐藏属性:
每个函数对象中都有一个公开的 prototype 属性,当你将这个函数作为构造函数来创建一个新的对象时,新创建对象的原型对象就指向了该函数的 prototype 属性。当然了,如果你只是正常调用该函数,那么 prototype 属性将不起作用。
现在我们知道了新对象的原型对象指向了构造函数的 prototype 属性,当你通过一个构造函数创建多个对象的时候,这几个对象的原型都指向了该函数的 prototype 属性,如下图所示:
这时候我们可以将 constant_temperature 属性添加到 DogFactory 的 prototype 属性上,代码如下所示:
function DogFactory(type,color){
this.type = type
this.color = color
//Mammalia
}
DogFactory. prototype.constant_temperature = 1
var dog1 = new DogFactory('Dog','Black')
var dog2 = new DogFactory('Dog','Black')
var dog3 = new DogFactory('Dog','Black')