JavaScript中的继承


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!