一 、 原型链继承
父类型实例赋值给子类型原型
function Animal(){
this.value='animal';
}
//父类型的方法
Animal.prototype.run=function(){
return this.value+''+'is runing';
}
function Cat(){}
//继承,用构造函数创建对象
//通过new 将构造函数的作用域赋给新对象,链接到原型
//绑定this,执行代码,添加属性,返回新对象
//Cat.prototype.__proto__=Animal.prototype
Cat.prototype=new Animal();//Cat.prototype 变成了 Animal 的一个实例,所以 Animal 的实例属性 names 就变成了 Cat.prototype 的属性。
var instance=new Cat();
instance.value='hellokitty';
console.log(instance.run());//hellokitty is run
原型链如图
此方法有以下缺点:
- 当原型链中包含引用类型值的原型时,该引用类型值会被所有实例共享
function Animal(){
this.names = ["cat", "dog"];//属性包含数组,即引用类型值
}
function Cat(){}
Cat.prototype = new Animal();
var instance1 = new Cat();
instance1.names.push("tiger");
console.log(instance1.names); // ["cat", "dog", "tiger"]
var instance2 = new Cat();
console.log(instance2.names); // ["cat", "dog", "tiger"]
- 子类型原型上的 constructor 属性被重写了,重写原型会失去默认constructor
if (Cat.prototype.constructor===Animal){
alert("true");//true
}
- 在创建子类型时,不能向超类型的构造函数中传递参数.
二 、借用构造函数(伪造对象OR经典方法)
在子类构造函数内部调用超类构造函数
用子类去创建实例时”借用”父类的构造函数
- 核心代码是Animal.call(this),创建子类实例时调用Animall构造函数,于是Cat的每个实例都会将Animall中的属性复制一份,使用父类的构造函数来增强子类实例,等同于复制父类的实例给子类
function Animal(){
this.color=["yellow","white","black"];
}
function Cat(){
Animal.call(this);//继承
}
var HelloKitty=new Cat();//cat相当于中间件,为了多复制超类
HelloKitty.color.push("pink");
console.log(HelloKitty.color);//[ "yellow", "white", "black", "pink" ]
- 保证了原型链中引用类型值的独立, 避免了引用类型的属性被所有实例共享
var HongMao=new Cat();
HongMao.color.push("red");
console.log(HongMao.color);//["yellow", "white", "black", "red" ]
- 可以直接在Child中向Parent传参
function Animal(gender){
this.gender=gender;
}
function Cat(gender){
Animal.call(this,"girl");//继承
Cat.age=18;//实例属性
}
var HelloKitty=new Cat();
console.log(HelloKitty.gender);//girl
缺点
- 方法都在构造函数中定义,每次创建实例都会创建一遍方法,每个子类都有父类实例函数的副本,影响性能
- 只能继承父类的实例属性和方法,不能继承原型属性/方法
- 不使用原型,无法实现函数复用
三 、组合继承(伪经典继承)
原型链及借用构造函数的组合
原型链实现继承原型属性与方法,借用构造函数实现实例属性的继承
- 定义父类属性及方法
function Animal(size){
this.size=size;
this.color=["yellow","white","black"]
}
//为原型对象添加方法
Animal.prototype.saysSize=function(){
console.log(this.size);
}
- 借用构造函数实现实例属性的继承,可添加子类属性
- 原型链实现继承原型属性与方法,可添加子类属性
//第二次调用父类构造器
function Cat(size ,gender){
Animal.call(this,size);// 借用构造函数 继承实例属性并添加自己的属性
this.gender=gender;
}
//第一次调用父类构造器
//Cat有一个size和color
Cat.prototype=new Animal();//原型链继承 方法
Cat.prototype.constructor=Cat;//矫正构造函数
Cat.prototype.sayGender=function(){//添加子类方法
console.log(this.gender);
}
- 实例
//实例化的时候,第二次调用Animal构造函数,会再创建一size和color
var HelloKitty=new Cat("little","girl");
HelloKitty.color.push("pink");
console.log(HelloKitty.color);//[ "yellow", "white", "black", "pink" ]
HelloKitty.sayGender();//girl
HelloKitty.saysSize();//little
var HongMao=new Cat("big","boy");
HongMao.color.push("red");
console.log(HongMao.color);//[ "yellow", "white", "black", "red" ]
HongMao.sayGender();//bot
HongMao.saysSize();//big
缺点
- 使用子类创建实例对象时,其原型中会存在两份相同的属性/方法
- 组合继承其实调用了两次父类构造函数, 造成了不必要的消耗
四、 原型式继承
利用一个空对象作为中介,将某个对象直接赋值给空对象构造函数的原型。
基于原有对象创建新对象,用Animal对象创建新对象。不用写function Animal()
{}
一个对象继承另一个对象的属性可用
- 原型式函数
实际是创建o.construct对应的function o(){}
function object(o){
function fn(){};//创建临时构造函数
fn.prototype=o;//关系创建,构造函数的原型对象是o
return new fn();//返回构造函数的实例
}
- 构造函数原型对象
var Animal={
value:animal,
size:["big","small"]
};
- 继承
var Cat=object(Animal);//==new Animal();
Cat.name="HelloKitty";//重写属性
Cat.size.push("mid");//添加引用类型值
console.log(Cat.name);//hellokitty
console.log(Cat.size) //[ "big", "small", "mid" ]
console.log(Animal.name);//[ "big", "small", "mid" ]
console.log(Animal.size);//animal
- ES5的另一种写法,使用Object.creat()方法
var Animal={
name:"animal",
size:["big","small"]
};
var Dog=Object.create(Animal);//==object(Animal),自带方法
Dog.name="Snoopy";//重写属性
Dog.size.push("little");//添加引用类型值
console.log(Dog.name);//Snoopy
console.log(Dog.size) //[ "big", "small", "mid" ,"litttle"]
缺点
引用类型值会被共享
寄生式继承
一个对象对另一个对象的继承,
改进的原型式 ,比原型式继承多了函数继承
- 创建一个封装继承过程的函数,内部可以创建函数等方式来增强对象,这一步也可使用Object.creat()
var Animal={
name:"animal",
size:["big","small"]
};
function object(o){
function fn(){};//创建临时构造函数
fn.prototype=o;//关系创建,构造函数的原型对象是o
return new fn();//返回构造函数的实例
}
- 创建一个”假“新对象,使用原型式方法,再添加函数。返回“假”新对象
function creat(ori){
var clone=object(ori);
clone.SayName=function(){
console.log(clone.name);
};
return clone//返回值
}
- 通过封装函数将“假”新对象赋给新对象(子类)
var Cat=creat(Animal);//将clone赋给Cat
console.log(Cat.size);// [ "big", "small" ]
Cat.name="HelloKitty";
Cat.SayName();//HelloKitty
缺点
- 和构造函数模式一样,无法函数复用 和原型式一样,无法传递参数和引用类型值实例共享
寄生组合式继承
寄生组合式是引用类型最理想的继承方式
结合借用构造函数传递参数和寄生模式实现继承
!传入的参数是构造函数!!!
- 继承函数
只需要调用一次父类构造函数
function inheritPrototype(son,father){//参数是子类型父类型的构造函数
var clone=Object.create(father.prototype);//创建超类原型的副本
clone.constructor=son;//为创建的副本添加constructor属性,矫正重写原型丢失的constructor
son.prototype=clone;//将副本赋值给子类的原型
}
- 父类属性及方法
function Animal(name){
this.name=name;
this.size=["big", "small"];
}
Animal.prototype.sayName=function(){
console.log(this.name);
- 借用构造函数继承属性
function Cat(name,color){
Animal.call(this,name);
this.color=color;
}
- 继承方法
inheritPrototype(Cat,Animal);
//原型式继承 增加子类方法
Cat.prototype.saySize=function(){
console.log(this.size);
}
var HelloKitty=new Cat("HelloKitty","Pink");
HelloKitty.sayName();//HelloKitty
HelloKitty.saycolor();//Pink
console.log(HelloKitty.size);//[ "big", "small" ]
来源:CSDN
作者:weixin_43585219
链接:https://blog.csdn.net/weixin_43585219/article/details/103927072