ES6以前
在es6以前 js 并没有专门的关键字可以实现继承特性,但是那时候我们又需要使用继承。于是就产生了一些方法可以实现继承的效果
构造函数继承
call调用实现继承父属性。我们都知道call
可以改变函数内this
的指向,利用这个特性我们可以在子构造函数里面调用父构造函数,并利用call
让父构造函数内的this
指向子构造函数。这样就完成了继承操作
function Father(uname,age){
this.name = uname;
this.age = age;
};
function Son(uname,age){
Father.call(this,uname,age); //调用父构造函数,利用call传入自身的this实现继承
}
重点:让新实例的原型等于父类的实例。 特点:1、实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新实例不会继承父类实例的属性!) 缺点:1、新实例无法向父类构造函数传参。 2、继承单一。 3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属性,另一个实例的原型属性也会被修改!)
原型链继承
我们还可以通过让子构造函数的prototype
指向一个父构造函数的实例的形式实现继承,不过需要注意的是在赋值完成后我们需要让constructor
属性重新指回子构造函数
function Father(uname,age){
this.name = uname;
this.age = age;
};
Father.prototype.say = ()=> console.log('hello!');
function Son(uname,age){
this.name = uname;
this.age = age;
}
// Son.prototype = Father.prototype 若我们直接赋值,会导致Son的原型对象和Father的原型对象是同一个,在Son中修改原型对象同时也会修改Father的。所以这样赋值是错误的
Son.prototype = new Father(); //通过一个Father实例化对象可以间接访问到Father的prototype
Son.prototype.constructor = Son; //让constructor指回Son构造函数,不然其会指向Father构造函数
let son = new Son('Asuka',16);
son.say(); //hello!
组合继承
function Father(uname,age){
this.name = uname;
this.age = age;
};
function Son(uname,age){
Father.call(this,uname,age); // 借用构造函数继承
}
Son.prototype = new Father(); // 原型链继承
const s11 = new Son('zhangsan',18)
ES6以后
es6中引入了class
关键字同时也引入了extends
关键字,利用这些关键字我们可以很轻易的实现继承。同时若我们想要调用父类的方法和属性只需要使用super
关键字。
class Father{
constructor(uname,age){
this.name = uname;
this.age = age;
}
say(){
console.log('hello!');
}
}
class Son extends Father{
constructor(uname,age,sex){
super(uname,age); //直接使用,调用父类constructor.一定要在this之前,不然SyntaxError
this.sex = sex;
}
say(){
super.say();
console.log('hi!');
}
}
let son = new Son('Asuhe',16,'male');
console.log(son); //Son { name: 'Asuhe', age: 16, sex: 'male' }
son.say(); // hello! hi!