来源|
第一种:原型链继承
利用原型链的特性进行继承
function Parent(){
this.name = 'web前端';
this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
console.log(this.name);
}
function Son(){};
Son.prototype = new Parent();
son1 = new Son();
son1.Say();
上面例子的解释:
①创建一个名为Parent的构造函数,暂且称为父构造函数,它有两个属性name、type
②通过Parent构造函数(即原型对象)的属性设置Say方法。 此时Parent有2个属性和1个方法
③创建一个名为Son的构造函数,暂且称之为子构造函数
④ 设置Son的属性(即原型对象)为父构造函数Parent的实例对象,即子构造函数Son继承父构造函数Parent。 此时Son也有2个属性和1个方法
⑤实例化Son构造函数,并将结果参数赋予变量son1,即son1是实例化对象,它也有2个属性和1个方法
⑥ 输出son1的Say方法,结果为“web backend”
优点:可以实现继承
缺点:
①因为Son.prototype(即原型对象)继承了Parent实例化对象,这就导致所有Son实例化对象都是一样的,它们都共享原型对象的属性和技能。 代码如下所示:
son1 = new Son();
son2 = new Son();
son1.type.push('VUE');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','VUE']
结果son1和son2都是['JS', 'HTML', 'CSS', 'VUE']
②子构造函数实例化对象,传递参数比较困难
第二种:构造函数继承
通过构造函数调用方法继承,直接看代码:
function Parent(){
this.name = 'web前端';
this.type = ['JS','HTML','CSS'];
}
function Son(){
Parent.call(this);
}
son1 = new Son();
son1.type.push('VUE');
console.log(son1.type);//['JS','HTML','CSS','VUE']
son2 = new Son();
console.log(son2.type);//['JS','HTML','CSS']
上面例子的解释:
①创建父构造函数Parent,它有两个属性:name和type
②创建子构造函数Son,并通过函数内部的call方法调用父构造函数Parent,实现继承
③分别创建构造函数Son的两个实例化对象son1和son2,向son1的type属性添加元素,但son2没有添加新元素,结果不同,说明已经实现了独立性
优势:
① 实现实例化对象的独立性;
② 还可以给实例化的对象添加参数
function Parent(name){
this.name = name;
}
function Son(name){
Parent.call(this,name);
}
son1 = new Son('JS');
console.log(son1);//JS
son2 = new Son('HTML');
console.log(son2);//HTML
缺点:
①方法全部定义在构造函数中,每次实例化对象都要重新创建,基本上很难实现函数复用
②call方法只调用父构造函数的属性和技巧css哪些属性可以继承,没有办法调用父构造函数的原型对象
第三种:组合继承
利用原型链继承和构造函数继承各自的优点结合使用,还是看代码:
function Parent(name){
this.name = name;
this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
console.log(this.name);
}
function Son(name){
Parent.call(this,name);
}
Son.prototype = new Parent();
son1 = new Son('张三');
son2 = new Son('李四');
son1.type.push('VUE');
son2.type.push('PHP');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','PHP']
son1.Say();//张三
son2.Say();//李四
上面例子的解释:
①创建一个名为Parent的构造函数,它有两个属性name和type
②通过Parent构造函数(即原型对象)的属性设置Say方法。 此时Parent有2个属性和1个方法
③创建子构造函数Son,并通过函数内部的call方法调用父构造函数Parent,实现继承
④子构造函数Son继承了父构造函数Parent,Son也有2个属性和1个方法
⑤分别创建构造函数Son的两个实例化对象son1和son2,传递不同的参数,在type属性中添加不同的元素,调用原型对象Say方法
优势:
① 使用原型链继承来实现原型对象的继承
② 使用构造函数继承来实现属性的继承,参数可以是
组合功能基本满足JS的继承,比较常用
缺点:
在任何一种情况下,父构造函数都会被调用两次:一次是在创建子构造函数的原型时,一次是在子构造函数内部
第四种:原型继承
创建一个将参数作为对象原型对象的函数
function fun(obj) {
function Son(){};
Son.prototype = obj;
return new Son();
}
var parent = {
name:'张三'
}
var son1 = fun(parent);
var son2 = fun(parent);
console.log(son1.name);//张三
console.log(son2.name);//张三
上面例子的解释:
①创建函数fun,内部定义一个构造函数Son
②设置Son的原型对象为参数,参数为对象,完成继承
③Son实例化并返回,即返回一个实例化的对象
优缺点:与原型链类似
第五种:寄生遗传
在原型继承的基础上,丰富函数内部的对象
function fun(obj) {
function Son() { };
Son.prototype = obj;
return new Son();
}
function JiSheng(obj) {
var clone = fun(obj);
clone.Say = function () {
console.log('我是新增的方法');
}
return clone;
}
var parent = {
name: '张三'
}
var parent1 = JiSheng(parent);
var parent2 = JiSheng(parent);
console.log(parent2.Say==parent1.Say);// false
上面例子的解释:
① 在原型继承的基础上封装一个吉生函数
② 改进fun函数返回的对象,添加Say方法,最后返回
③调用JiSheng函数两次,分别将参数赋予变量parent1和parent2
④比较parent1和parent2,结果为假,实现独立
优缺点:与构造函数继承类似css哪些属性可以继承,调用函数需要重新创建,无法实现函数复用,效率较低
这里补充一个知识点,ES5新增了一个方法Object.create(),相当于封装了原型继承。 该方法可以接收两个参数:第一个是新对象的原型对象(可选),第二个是新对象的新属性,所以前面的代码也可以是这样的:
function JiSheng(obj) {
var clone = Object.create(obj);
clone.Say = function () {
console.log('我是新增的方法');
}
return clone;
}
var parent = {
name: '张三'
}
var parent1 = JiSheng(parent);
var parent2 = JiSheng(parent);
console.log(parent2.Say==parent1.Say);// false
第六类:寄生组合继承
利用组合继承和寄生继承
关于组合继承方式我们已经说过了,它的缺点是父构造函数被调用了两次,一次是在创建子原型时,另一次是在子构造函数内部,那么我们只需要优化这个问题即可。 ,即减少对父构造函数的一次调用,只需利用寄生继承的特性继承父构造函数的原型来创建子原型即可。
function JiSheng(son,parent) {
var clone = Object.create(parent.prototype);//创建对象
son.prototype = clone; //指定对象
clone.constructor = son; //增强对象
}
function Parent(name){
this.name = name;
this.type = ['JS','HTML','CSS'];
}
Parent.prototype.Say=function(){
console.log(this.name);
}
function Son(name){
Parent.call(this,name);
}
JiSheng(Son,Parent);
son1 = new Son('张三');
son2 = new Son('李四');
son1.type.push('VUE');
son2.type.push('PHP');
console.log(son1.type);//['JS','HTML','CSS','VUE']
console.log(son2.type);//['JS','HTML','CSS','PHP']
son1.Say();//张三
son2.Say();//李四
上面例子的解释:
① 封装一个函数 JiSheng,有两个参数,参数1为子构造函数,参数2为父构造函数
② 使用Object.create()克隆父构造函数的原型作为克隆
③ 使用副本作为子构造函数的原型
④在副本中添加构造函数属性,因为更改③中的原型会导致副本失去默认属性
的优点和缺点:
目前JS继承中采用了组合继承和寄生继承的优点。
总结
为了更好地理解JS继承方法,你必须了解构造函数、原型对象、实例化对象、原型链等概念。
我是@半糖学入头,专注于后端技术领域的分享,一名后端从业者,关注我,跟我一起学习,共同进步!
发表评论