继承与原型链
1.继承
大部分语言都支持两种继承方式:接口继承和实现继承。接口继承只继承方法签名,实现继承则继承实际的方法。
在ECMAScript中,无法实现几口继承,只支持实现继承。
在ECMAScript中,实现继承主要是依靠原型链来实现。
2.构造函数、原型和实例的关系
前面我们已经说到,在ECMAScipt张,是通过原型链来实现继承的。在了解原型链之前,我们先了解构造函数、原型和实例对象的关系,这有助于帮助理解原型链。
原型:每个实例对象中,都有一个属性__proto__
是原型,浏览器使用,不标准的属性;
原型的作用:数据共享,节省内存空间
原型的写法:
构造函数.prototype.属性=值; //属性共享
构造函数.prototype.方法=值; //方法共享
每个构造函数都有一个属性protptype
是原型,程序员使用的。
function F () {}
console.log(F.prototype) // => object
F.prototype.sayHi = function () {
console.log('hi!')
}
构造函数的 prototype 对象默认都有一个 constructor 属性,指向 prototype 对象所在函数。
console.log(F.constructor === F) // => true
通过构造函数得到的实例对象内部会包含一个指向构造函数的 prototype 对象的指针__proto__
。
var instance = new F()
console.log(instance.__proto__ === F.prototype) // => true
__proto__
是非标准属性。
实例对象可以直接访问原型对象成员。
instance.sayHi() // => hi!
如下图:
总结:构造函数、原型和实例的关系:
- 任何函数都具有一个
prototype
属性,该属性是一个对象,即构造函数的原型对象; - 构造函数的
prototype
原型 对象默认都有一个constructor
构造器属性,指向prototype
原型对象所在的构造函数 - 实例对象的原型对象
__proto__
指向的是该构造函数的原型对象 - 构造函数的原型对象
prototype
中的方法可以被实例对象直接访问 - 所有实例都直接或间接继承了原型对象的成员
3.原型链
原型链:基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
前面已经提到构造函数、原型和实例对象的关系:每个构造函数中都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。
假如让原型对象等于另一个类型的实例,那么此时的原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含一个指向另一个构造函数的指针。假如,另一个原型又是另一个类型的实例,那么关系依然成立,就构成了实例与原型的链条,这就是原型链的基本概念;
原型链的搜索机制:
当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型;再通过原型链实现继承的情况下,搜索过程就会沿着原型链继续向上搜索。