与大部分面向对象语言不一样,JavaScript中并无引入类(class)的概念,但JavaScript仍然大量地使用了对象,为了保证对象之间的联系,JavaScript引入了原型与原型链的概念。函数
在Java中,声明一个实例的写法是这样的:ui
ClassName obj = new ClassName()
复制代码
为了保证JavaScript“看起来像Java”,JavaScript中也加入了new操做符:this
var obj = new FunctionName()
复制代码
能够看到,与Java不一样的是,JavaScript中的new操做符后面跟的并不是类名而是函数名,JavaScript并不是经过类而是直接经过构造函数来建立实例。spa
function Dog(name, color) {
this.name = name
this.color = color
this.bark = () => {
console.log('wangwang~')
}
}
const dog1 = new Dog('dog1', 'black')
const dog2 = new Dog('dog2', 'white')
复制代码
上述代码就是声明一个构造函数并经过构造函数建立实例的过程,这样看起来彷佛有点面向对象的样子了,但实际上这种方法还存在一个很大的问题。prototype
在上面的代码中,有两个实例被建立,它们有本身的名字、颜色,但它们的bark方法是同样的,而经过构造函数建立实例的时候,每建立一个实例,都须要从新建立这个方法,再把它添加到新的实例中。这无疑形成了很大的浪费,既然实例的方法都是同样的,为何不把这个方法单独放到一个地方,并让全部的实例均可以访问到呢。设计
这里就须要用到原型(prototype):code
Object.getPrototypeOf(obj)
或 obj.__proto__
来访问。对象.__proto__ === 函数.prototype
。那么能够将上述代码稍微作些修改,这里把bark方法放入Dog构造函数的原型中:cdn
function Dog(name, color) {
this.name = name
this.color = color
}
Dog.prototype.bark = () => {
console.log('wangwang~')
}
复制代码
接着再次经过这个构造函数建立实例并调用它的bark方法:对象
const dog1 = new Dog('dog1', 'black')
dog1.bark() //'wangwang~'
复制代码
能够看到bark方法可以正常被调用。这时再建立另外一个实例并重写它的bark方法,而后再次分别调用两个实例的bark方法并观察结果:blog
const dog2 = new Dog('dog2', 'white')
dog2.bark() = () => {
console.log('miaomiaomiao???')
}
dog1.bark() //'wangwang~'
dog2.bark() //'miaomiaomiao???'
复制代码
这里dog2重写bark方法并无对dog1形成影响,由于它重写bark方法的操做其实是为本身添加了一个新的方法使原型中的bark方法被覆盖了,而并不是直接修改了原型中的方法。若想要修改原型中的方法,须要经过构造函数的prototype属性:
Dog.prototype.bark = () => {
console.log('haha~')
}
dog1.bark() //'haha~'
dog2.bark() //'haha~'
复制代码
这样看起来就没什么问题了,将实例中共有的属性放到原型对象中,让全部实例共享这部分属性。若是想要统一修改全部实例继承的属性,只须要直接修改原型对象中的属性便可。并且每一个实例仍然能够重写原型中已经存在的属性来覆盖这个属性,而且不会影响到其余的实例。
上文提到,JavaScript中全部的对象都是由它的原型对象继承而来。而原型对象自身也是一个对象,它也有本身的原型对象,这样层层上溯,就造成了一个相似链表的结构,这就是原型链(prototype chain)。
全部原型链的终点都是Object函数的prototype属性,由于在JavaScript中的对象都默认由Object()构造。Objec.prototype指向的原型对象一样拥有原型,不过它的原型是null,而null则没有原型。
经过原型链就能够在JavaScript中实现继承,JavaScript中的继承至关灵活,有多种继承的实现方法,这里只介绍一种最经常使用的继承方法也就是组合继承。
function Dog(name, color) {
this.name = name
this.color = color
}
Dog.prototype.bark = () => {
console.log('wangwang~')
}
function Husky(name, color, weight) {
Dog.call(this, name, color)
this.weight = weight
}
Husky.prototype = new Dog()
复制代码
这里声明了一个新的构造函数Husky,经过call方法继承Dog中的属性(call方法的做用能够简单理解为将Dog中的属性添加到Husky中,由于还涉及到其余的知识点因此很少赘述),并添加了一个weight属性。而后用Dog函数建立了一个实例做为Husky的原型对象赋值给Husky.prototype以继承方法。这样,经过Husky函数建立的实例就拥有了Dog中的属性和方法。
若是想要深刻了解关于JavaScript中的对象和原型链的话,无脑推荐红宝书(《JavaScript高级程序设计(第3版)》)吧,第六章关于原型链有至关详细的讲解。