继承是面向对象编程很重要的一个方面,让子类继承父类的某些属性和方法,是非常常见的需求。
prototype写法
假设一个构造函数
function Human(name) {
this.name = name;
}
Human.prototype.say = function () {
console.log('hi')
}
let xh=new Human('xh');
console.log(xh.name); //'xh'
xh.say(); //'hi'
Human是个构造函数,它接受一个参数name,把name作为实例的name属性,Human的prototype上有一个say方法,会打印出‘hi’,所有Human的实例共享这个方法。
另一个构造函数
function Man(age) {
this.age = age;
this.fight = function () {
console.log('fight');
}
}
let xm=new Man(18);
console.log(xm.age); //18
xm.fight(); //'fight'
它接受一个参数,作为实例的age属性,同时定义实例的fight方法。
此时Man类没有定义say方法,其实例直接调用会报错:
xm.say(); //Uncaught TypeError: xm.say is not a function
如果要让子类Man继承父类Human的say方法,可以这样写:
Man.prototype.__proto__ = Human.prototype;
然后子类Man的实例再调用say方法:
xm.say(); //'hi'
同时可以赋予子类实例某些属性,在子类Man构造函数内部call父类构造函数
function Man(name,age) {
this.age = age;
Human.call(this,name);
this.fight = function () {
console.log('fight');
}
}
let xm = new Man('xm',18);
console.log(xm.name); //'xm'
console.log(xm.age); //18
这样子类也有了name属性。
不过此写法不符合ES6标准,可以这样写:
Object.setPrototypeOf(Man.prototype,Human.prototype);
上述代码将Human.prototype设为Man.prototype的原型。
或者用一种变通的写法:
let fn=function(){};
fn.prototype=Human.prototype;
Man.prototype=new fn();
上述代码通过new运算符,在构造函数fn内部,将Man.prototype的原型指向了fn.prototype,也就是指向了Human.prototype。
这三种写法都能让Man.prototype.__proto__ 指向 Human.prototype
console.log(Man.prototype.__proto__ === Human.prototype); //true
不过上述写法会让Man的实例constructor属性指向Human
console.log(xm.constructor.name); //'Human'
应该重新指定一下constructor属性
Man.prototype.constructor=Man;
console.log(xm.constructor.name); //'Man'
class写法
用class写法,上述过程简单很多
class Human {
constructor(name) {
this.name = name;
}
say() {
console.log('hi')
}
}
let xh = new Human('xh');
console.log(xh.name); //'xh'
xh.say(); //'hi'
class Man extends Human {
constructor(age, name) {
super(name);
this.age = age;
}
fight(){
console.log('fight');
}
}
let xm=new Man(18,'xm');
console.log(xm.name); //'xm'
console.log(xm.age); //18
xm.say(); //'hi'
xm.fight(); //'fight'
在子类Man中,可以使用super关键字来调用超类Human的方法。