继承、“圣杯模式”
一、为啥继承
大概的思路就是某一个对象(Child)要能够继承来自于另一个对象(Parent)的属性和方法。
二、继承的几种方法
1. 借用构造函数
function Person(name,age,sex){
this.name = name;
this.age = age;
this.sex = sex;
}
function Student(name,age,sex,grade){
Person.call(this,name,age,sex);
this.grade = grade;
}
var student = new Student();
具体是使用 call/apply,实现借用别人的方法来实现自己的功能
不能继承借用构造函数的原型,每次构造函数都要多走一个函数
2. 传统形式——原型链
将 Child 的 prototype 指向一个 Parent 的实例,实现继承,Child.prototype = new Parent(),这样做之后, Child.prototype 上的属性会被 Parent 实例上的属性覆盖,Child.prototype 上原本的constructor 属性也一样,这时候会变成 Child.prototype.constructor = Parent,
因此,要特别注意
,改变 Child.prototype 之后,要手动把他的指向归位 Child.prototype.constructor = Child
一层一层继承下来之后,过多地继承了没用的属性
Grand.prototype.lastName = '张三';
function Grand(){ }
var grand = new Geand();
Father.prototype = grand;
function Father(){
this.name = '王五';
}
var father = new Father();
Son.prototype = father;
function Son(){
this.hobbit = '赵六';
}
var son = new Son()
3. 共享原型
在第二种方式上改进,直接继承 prototype,让两个构造函数的原型都指向同一个,因为原型地址相同,不能随便改动自己的原型,无法实现某一个的特殊功能,由此在此基础上引出了最完美的圣杯模式
Father.prototype.lastName = '张三';
function Father(){
}
function Son(){
}
Son.prototype = Father.prototype;
var son = new Son();
var father = new Father();
4. 圣杯模式
圣杯模式的出现和思路与浅层克隆和深层克隆问题很相似,都是引入了一个中间的媒介,利用空对象作为中介,空对象几乎不占内存,让空对象的原型指向父亲的原型,让儿子的原型不是直接指向父亲,而是指向空对象的实例,这样也能继承到父亲上的属性,改变时又不会互相影响,最后记得将儿子的构造器归位就可以
function inherit(Child, Parent){
function F(){}
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
}
关系图变成了如下,红箭头表示人为地引用使地址相同,蓝箭头表示原型链上的继承关系。把红箭头看成是等于号会更好理解一点😅,因为引用值相同了已经。
因为对 Target.prototype 进行了操作,Target.prototype.constructor已经变了。因此需要归位,同时设置一个超类,可以查到 Target.prototype 最终继承自谁,可有可无。
function inherit(Target, Origin){
function F(){};
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;//归位
Target.prototype.uber = Origin.prototype;//超类
}
最后一步,用一个立即执行函数,把媒介 function F(){} 有关的变成闭包,私有化,外面访问不了,这就是最终版的圣杯模式。
var inherit = (function(){
var F = function(){};
return function(Target,Origin){
F.prototype = Origin.prototype;
Target.prototype = new F();
Target.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}
}())
inherit(Son,Father)
var son = new Son();
var father = new Father();
这时,再对 Target.prototype 添加一些自己的属性和方法也不会影响到 Origin
5. 拷贝继承
就是直接把 Parent 上不变的属性写在原型上,再用拷贝函数将父对象的prototype对象中的属性,一一拷贝给Child对象的prototype对象,这就涉及到了深拷贝浅拷贝的问题了
参考:https://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html