继承: 子类继承父类的属性和方法(要继承,必须要有父类)。
//父类
function Person(a) { //给父类构造函数添加参数
this.name = a;
this.age = null;
this.sex = "男";
this.sleep = function () {
return "睡觉";
}
}
Person.prototype.height = 100; //给父类的构造函数添加原型属性
javascript中的每个对象都有
prototype
属性,Javascript中对象的prototype属性的解释是:返回对象类型原型
的引用。
一、原型链继承(常用)
什么是原型链继承?
由外到内,通过
__proto__
属性连接,形成链式,就叫原型链。
原型链继承是通过prototype
属性实现继承的。
var arr = new Array();
//实例化数组对象arr 继承了Array类对象的属性和方法
console.log(arr);
console.log(Math);
通过上面两个内置对象可以了解到,js底层就是通过原型链继承而来的。
原型链继承的特点:
1.子类的实例继承:子类的构造属性和方法、父类的构造属性和方法、父类的原型属性和方法
2.子类对象的实例既是子类的实例,也是父类的实例。
3.父类的构造函数只执行一次
缺点:
1.不能继承子类的原型属性和方法(在继承之前给子类添加原型属性和方法无效)
2.只能进行单继承,不能多继承(继承单一)
3.继承之后,原型对象上的属性全是共享的(所有新实例都会共享原型对象上的属性)
4.新实例无法向父类构造函数传参。
举例:
//父类对象的构造函数
function Person(a) {
this.name = a; //this指向当前类的实例化对象
this.age = null;
this.sex = "男";
this.sleep = function () {
return "睡觉";
}
}
Person.prototype.height = 100;
//子类对象的构造函数
function Child() {
this.job = null;
}
//原型链继承不能直接继承子类的原型属性和方法 简单说相当于被覆盖了(Child.prototype=new Person();)
//即:在原型链继承之前 给子类添加原型属性和方法无效
Child.prototype.eat = function () {
return "吃饭";
};
//原型链继承(在实例化子类之前) 核心是让子类的原型等于父类的实例
Child.prototype = new Person("张三");
//在继承之后给子类添加原型属性和方法 是直接添加给子类的原型对象(不会添加给父类)
Child.prototype.weight = 50;
//实例化子类对象
var child = new Child();
var child1 = new Child();
console.log(child, child1); //原型对象上的属性共享
console.log(Child.prototype); //没有原型链继承,子类的原型对象指向自身
console.log(Child.prototype); //原型链继承之后,子类的原型对象指向父类
//继承之后对象的问题
//obj instanceof class 用来测试一个对象obj是否为另一个类class的实例
console.log(child instanceof Person); //true
console.log(child instanceof Child); //true
//子类实例child的原型链
console.log(child.__proto__); //Person
console.log(child.__proto__.__proto__); //Person的原型对象
console.log(child.__proto__.__proto__.__proto__); //Object的原型对象
console.log(child.__proto__.__proto__.__proto__.__proto__); //Object 的 __proto__ null
1.类对象的构造函数里边的this 指向当前类的实例化对象。
2.原型链继承的核心,就是让子类的原型(prototype)等于父类的实例。
3.如果不设置原型对象,直接实例化,默认继承Object,原型对象的构造函数constructor指向自身。
4.构造函数在类被实例化的时候直接执行。
5.在继承之后给子类添加原型属性和方法,是直接添加给子类的原型对象(不会添加给父类)
二、构造继承(常用)
什么是构造继承?
构造继承是通过
.call()
/.apply()
来实现继承的。(用.call()
和.apply()
将父类构造函数引入子类函数,即:在子类函数中做了父类函数的自执行(复制))。
优点:
1.可以实现多继承。
2.可以向父类传递参数。
3.解决了原型链继承中原型属性共享的缺点
缺点:
1.只能继承父类构造函数的属性和方法,无法继承父类的原型属性和方法
2.子类对象是子类的实例,不是父类的实例
3.无法实现构造函数的复用(每次用的时候都要重新调用)
4.每个新实例都有父类构造函数的副本,臃肿。
举例:
//父类一
function Animal(){
this.name=arguments[0];
this.sleep=function(){
return "睡觉";
}
}
Animal.prototype.color=null;
//父类二
function Type(){
this.type=arguments[0];
}
//子类
function Dog(n,s,t){
this.sex=s;
this.eat=function(){
return "吃饭";
};
//这里写构造继承
Animal.call(this,n);
Type.apply(this,[t])
}
//实例化子类对象 传参
var dog=new Dog("小黑","母的","犬科");
console.log(dog);
//子类对象是子类的实例 不是父类的实例
console.log(dog instanceof Dog); //true
console.log(dog instanceof Animal); //false
console.log(dog instanceof Type); //false
//子类的原型对象
console.log(Dog.prototype); //构造函数指向自身
//子类实例的原型链
console.log(dog.__proto__); //Object Dog的原型对象
console.log(dog.__proto__.__proto__); //Object Object的原型对象
三、实例继承
什么是实例继承?
在子类内部直接构造父类的实例,直接new父类。
优点:
不限制调用方式(子类实例有两种写法)
可以向父类传递参数
缺点:
不能实现多继承
不能拿到子类构造属性和方法
子类对象的实例不是子类的实例,而是父类的实例
举例:
//父类
function Person(n,s){
this.name=n;
this.sex=s;
this.sleep=function(){
return "睡觉";
}
}
//子类
function Child(n,s){
this.eat=function(){
return "吃饭";
};
return new Person(n,s);
}
//实例化子类对象 两种写法
var child=Child("张三","男");
console.log(child); //不能拿到子类的构造属性和方法 eat
var child1=new Child("张三","男");
console.log(child1); //不能拿到子类的构造属性和方法 eat
//检测子类对象的实例 是父类的实例 不是子类的实例
console.log(child instanceof Child); //false
console.log(child instanceof Person); //true
//子类的原型对象
console.log(Child.prototype); //自身
//子类对象实例的原型链
console.log(child.__proto__); //person的原型对象
console.log(child.__proto__.__proto__); //Object的原型对象
四、拷贝继承
什么是拷贝继承?
在子类里面,将父类里面的方法和属性拷贝给子类的原型对象 prototype
。
优点:
支持多继承
可以继承父类构造函数的属性和方法、也可以继承父类原型对象的属性和方法
子类对象的实例是子类的实例,不是父类的实例
可以向父类传递参数
缺点:
在继承的时候不断new,占内存
举例:
//父类一
function Animal(){
this.name=arguments[0];
this.sleep=function(){
return "睡觉";
}
}
Animal.prototype.color="black";
//父类二
function Type(){
this.type=arguments[0];
}
//子类
function Cat(n,s,t){
this.sex=s;
this.eat=function(){
return "吃饭";
};
//拷贝父类里边的属性和方法 拷贝给prototype
//先实例化父类对象 传参
var animal=new Animal(n);
for(var key in animal){
Cat.prototype[key]=animal[key]; //注意这里的Cat不能写成this,因为这里的this指向是类的实例化对象,没有prototype属性
}
var type=new Type(t);
for(var key in type){
Cat.prototype[key]=type[key];
}
}
//实例化子类对象
var cat=new Cat("小花","母","猫科");
console.log(cat);
//子类的原型对象
console.log(Cat.prototype); //自身
//检测子类对象的实例 是子类的实例 不是父类的实例
console.log(cat instanceof Cat); //true
console.log(cat instanceof Animal); //false
console.log(cat instanceof Type); //false
//子类对象实例的原型链
console.log(cat.__proto__); //Cat的原型对象
console.log(cat.__proto__.__proto__); //Object的原型对象
五、组合继承
什么是组合继承?
原型链继承+构造继承
互相弥补对方的缺点,结合了两种方式的优点
优点:
子类对象的实例既是子类的实例,也是父类的实例
可以实现多继承(但是只能继承其中一个父类的原型属性)
可以向父类传递参数
每个新实例引入的构造函数属性是私有的(构造函数的属性和方法不是共享的)。
缺点:
调了两次父类的构造函数(耗内存),会有重复属性
举例:
//父类
function Person(n){
this.name=n;
this.eat=function(){
return this.name+"吃饭";
}
}
Person.prototype={
constructor:Person,
color:null
};
//子类
function Child(a,s,n){
this.age=a;
this.sex=s;
this.sleep=function(){
return "睡觉";
};
//构造继承 只能继承父类构造函数的属性和方法 不能继承父类原型对象的属性和方法
Person.call(this,n)
}
//原型链继承
//父类的构造函数属性和方法、父类原型属性和方法全部传给子类原型对象
Child.prototype=new Person(); //注意 这里没传值
//实例化子类对象
var child=new Child(12,"女","小花");
console.log(child);
var child1=new Child(13,"男","小明");
console.log(child1);
//检测子类对象实例 是子类的实例 也是父类的实例
console.log(child instanceof Child); //true
console.log(child instanceof Person); //true
//子类对象实例的原型链
console.log(child.__proto__); //Person
console.log(child.__proto__.__proto__); //Person的原型对象
console.log(child.__proto__.__proto__.__proto__); //Object的原型对象
六、寄生组合继承
寄生组合继承处理了组合继承的缺点,避免调用两次父类的构造函数,避免重复属性和方法。
原理:
将父类的原型对象给一个空对象的prototype,再把空对象和子类的原型对象关联。
举例:
//父类
function Person(){
this.name=arguments[0];
this.sleep=function(){
return "睡觉";
}
}
Person.prototype={
constructor:Person,
color:null
};
//子类
function Child(n,a){
this.age=a;
this.eat=function(){
return "吃饭";
};
//构造继承
Person.call(this,n)
}
(function(){
var fn=function(){};
//寄生
fn.prototype=Person.prototype;
//原型链继承
Child.prototype=new fn();
})();
//实例化子类对象
var child=new Child("张三",12);
console.log(child);
//检测子类的实例化对象
console.log(child instanceof Child); //true
console.log(child instanceof Person); //true
//子类实例化对象的原型链
console.log(child.__proto__); //Person
console.log(child.__proto__.__proto__); //Person的原型对象
console.log(child.__proto__.__proto__.__proto__); //Object