继承
原型链继承
ECMAScript 中描述了原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原 型让一个引用类型继承另一个引用类型的属性和方法构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型 对象的内部指针。那么,假如我们让原型对象等于另一个类型的实例,结果会怎么样呢?显然,此时的 原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数 的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实 例与原型的链条。这就是所谓原型链的基本概念。
通过例子深入理解
function Father() {
this.fatherage = 45
}
Father.prototype.getFatherAge = function () {
return this.fatherage
}
function Son() {
this.sonage = 20
}
Son.prototype.prosonage = 20; //在此处添加属性 Son的原型对象上没有此属性 why???
Son.prototype.getSonAge = function () { //在此处添加方法 Son的原型对象上没有此方法 why???
return this.sonage
}
Son.prototype = new Father();
Son.prototype.getSonAge = function () {
return this.sonage
}
var relationship = new Son();
console.log(relationship);
通过控制台打印构造函数Son的实例对象 relationship 可以看到
relationship对象包含了自身的属性sonage和一个__proto__属性,该属性是一个普通对象,指向的
是构造函数Son的原型对象,上面代码中我们将 Son的原型对象等于Father的实例,所以Son的原型对象中
会包含一个指向Father原型对象的指针,即 Son.prototype.proto === Father.prototype
Father原型对象包含一个指向构造函数(Father)的指针constructor
特别注意
此时 relationship的 cunstructor属性指向的是Father,这是因为 Son的原型指向了另一个对象—— Father 的原型,而这个原型对象的 constructor 属性指向的是 Father。 Son.prototype.constructor === Father //true
所有的函数默认原型都是Object的实例,所以Father的原型对象会包含一个指向Object原型的指针, 即 Father.prototype.proto === Object.prototype,所以所有自定义类型都会继承 toString()、 valueOf()等默认方法 Object.prototype.proto 指向null
这个例子中的实例以及构造函数和原型之间的关系如下图所示
原型搜索机制。读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。 就拿上面的例子来说,调用 relationship.getFatherAge()会经历三个搜索步骤:1)搜索实例;2)搜索 Son.prototype; 3)搜索 Father.prototype,最后一步才会找到该方法。 在找不到属性或方法的情况下,搜索过 程总是要一环一环地前行到原型链末端才会停下来。
当创建一个函数的时候,会自动为该函数创建一个prototype属性,这个属性指向函数的原型对象,所有的原型对象都有一个constructor属性,该属性指向函数本身
所以可以这样来理解 ,原型对象的construtor属性指向函数本身,函数本身有个一
prototype属性又会指向函数的原型对象,从控制台中可以看出