js继承的方法和实现原理
ES6之前并没有专门用于继承的关键字。ES6之后有了Class
和extends
实现继承首先需要一个父类;(继承谁,提供继承的属性)
//父类
function Person(name,age){
this.name = name;
this.age = age;
}
1.原型链继承
将子类的原型对象 修改为 父类的实例对象。(让新实例的原型等于父类的实例)
Son.prototype = new Father();//重要
//父类
function Person(name,age){
this.name = name;
this.age = age;
}
//来个子类
function Teacher(jobTitle,pay){
this.jobTitle = jobTitle;
this.pay = pay;
}
//实例化一个Person对象,将对象的地址赋值给Teacher的prototype属性。(实现继承)
//【注意】Teacher.prototype原来指向的对象就没有了,指向新的Person实例对象。
Teacher.prototype = new Person("马老师",30);
//构造实例
var t1 = new Teacher("体育",3000);
console.log(t1.name)
【缺点】:
1.对复杂数据类型(引用数据类型)的修改会出现误差
2.继承来的属性的值都是统一的。
2.构造函数继承
在子类的构造函数中,普通方法调用父类的构造函数(复制父类的实例属性给子类),需要使用call
或者apply
来修改,构造函数的this
指向
function Son(param1,param2){
Father.call(this,param1,param2);
}
//父类
function Person(name,age){
this.name = name;
this.age = age;
this.eat = function() {
console.log("干饭人干饭魂,干饭都得用大盆");
}
}
//来个子类
function Teacher(name,age,pay){
Person.call(this,name,age);
this.pay = pay;
}
//构造实例
var t1 = new Teacher("马老师",40,3000);
var t2 = new Teacher("张老师",25,10000);
console.log(t1.eat == t2.eat);//false 两个不同的函数不相等
【缺点】:
1.继承来的方法只能定义在构造函数中,不能定义在父类的原型对象上
2.继承来的函数无法复用,占用内存比较大。
3.组合继承
结合了原型链继承和借用构造函数继承两者的优点;
1.在子类的构造函数中借用父类的构造函数 father.call(this);
2.将父类的方法定义在父类的原型对象上
3.将子类的原型对象指向 父类的实例对象
4.修正constructor
属性
//父类
function Person(name, age) {
this.name = name;
this.age = age;
this.emotion = ['喜', '怒', '哀', '乐']
}
//将父类的方法定义在父类的原型对象上
Person.prototype.eat = function() {
console.log("干饭人干饭魂,干饭都得用大盆");
}
//来个子类
function Teacher(name, age, pay) {
//将子类的构造函数中借用父类的构造函数
Person.call(this, name, age)
this.pay = pay;
}
//将子类的原型对象指向 父类的实例对象
Teacher.prototype = new Person();
//Teacher的原型对象已经被修改为Person的实例对象。Person的实例对象是没有construtor属性的,所以下面这个代码会输出false
console.log(Teacher.prototype.constructor == Teacher); //false
//修正Teacher的原型对象上constructor属性的指向
Teacher.prototype.constructor = Teacher;
var t1 = new Teacher("王老师", 30, 5000);
console.log(t1.name); //可以继承父类的属性
t1.eat(); //可以继承 父类原型对象上的方法。
t1.emotion.push("愁");
console.log(t1.emotion);
var t2 = new Teacher("周老师", 35, 7000);
console.log(t2.emotion); //解决了 原型链继承中 对复杂数据类型修改出现误差的情况
console.log(t1); // name age emotion pay 在t1这个实例对象上都有。
实际上是借用了构造函数,以覆盖的方式,解决了在原型链继承中原型的引用类型属性共享在所有实例中的问题。
【缺点】:
调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数