面向对象之继承

从原型链 –> 继承
ES5的继承
ES6的继承

原型链

ps:prototype叫做原型,理解为“公有属性/方法”

1
2
3
4
5
6
7
8
9
10
11
12
var arr = new Array();
arr.push();
arr.valueOf();
arr.__proto__指向Array的公有属性(Array.prototype)
Array.prototype上有个push方法,因此arr顺着原型链找到push方法
Array.prototype.__proto__指向Object的共有属性(Object.prototype),上面有valueOf()方法,因此arr顺着原型链找到了valueOf方法
Object.prototype.__proto__指向null

JS中没有类的概念,因此也就没有继承的说法。
因此,只能把构造函数看作类
本身公有属性里不存在的,需要顺着原型链找的属性/方法看作继承
如果硬要说的话,arr的valueOf方法继承自Object
arr的push方法不是继承来的,而是实例属性(因为arr是个数组,而push是数组本来就有的公有属性,但valueOf与数组无关)

把构造函数看作类

ES6之前,JS没有类的概念,因此只得把构造函数当做类

1
2
function Human(){}
var person = new Human();

继承

继承可以使得子类具有父类所有的属性和方法。

ES5的继承

  • 首先,声明一个父类Human
    1
    2
    3
    4
    5
    6
    7
    function Human(name){
    this.name=name;
    }
    Human.prototype.say=function(){
    console.log('我叫'+this.name);
    }
    var stage = new Human('stage');

  • 然后,声明一个子类Male
    1
    2
    3
    4
    5
    6
    7
    function Male(){
    this.gender = "男性";
    }
    Male.prototype.hobby = function(){
    console.log("男生喜欢玩电动");
    }
    var Jack = new Male();

  • 接着,让子类Male继承父类Human的私有属性
    1
    2
    3
    4
    5
    function Male(name,age){
    Human.call(this,name);//将父类的私有属性name传过来
    this.gender = "男性";
    this.age = age;
    }


现在,子类Male继承了父类Human的属性,只差让子类的 原型 链接到 父类的prototype

  • 最后,让子类继承父类的公有属性(prototype)
    1
    2
    Male.prototype.__proto__ = Human.prototype;
    //Male的 原型 链接到 Human的 原型


至此,子类Male继承了父类Human的name属性和say()方法

最终写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//父类
function Human(name){
this.name=name;
}
Human.prototype.say=function(){
console.log('我叫'+this.name);
}
//子类
function Male(name,age){
Human.call(this,name);//将父类的私有属性name传过来
this.gender = "男性";
this.age = age;
}
Male.prototype.hobby = function(){
console.log("男生喜欢玩电动");
}
Male.prototype.__proto__ = Human.prototype;
var stage = new Male('stage',22);

兼容IE的继承

由于IE11以下不支持Male.prototype.__proto__ = Human.prototype;所以这一句要改写成

1
2
3
function Fn(){} //声明一个空的构造函数
Fn.prototype = Human.prototype;
Male.prototype = new Fn();

因此,ES5兼容IE的继承写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//父类
function Human(name){
this.name=name;
}
Human.prototype.say=function(){
console.log('我叫'+this.name);
}
//子类
function Male(name,age){
Human.call(this,name);//将父类的私有属性name传过来
this.gender = "男性";
this.age = age;
}
//将父类的公有属性传过来
function Fn(){} //声明一个空的构造函数
Fn.prototype = Human.prototype;
Male.prototype = new Fn();
Male.prototype.hobby = function(){
console.log("男生喜欢玩电动");
}
var stage = new Male('stage',22);

new做了些什么

  1. var this = {} 产生一个空对象,且this指向这个空对象
  2. this.proto = 构造函数.prototype【举例:var stage = new Human(‘stage’),则stage.proto === Human.prototype】
  3. 执行构造函数.apply(this,arguments)
  4. return this(生成的对象)

ES6的继承

在ES6中,将私有属性写在constructor里面,公有属性写在外面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//定义父类
class Human{
constructor(name){
//私有属性
this.name = name;
};
//公有属性
say(){
console.log("我叫"+this.name);
};
}
//子类
class Male extends Human{//将子类的prototype.__proto__链接到Human的公有属性
constructor(name,age){
super(name); //将父类的私有属性name传进来
this.gender = '男';
this.age = age;
};
hobby(){
console.log('男生喜欢玩电动');
};
}
let stage = new Male('stage',22);

ES6中class的缺陷

在ES5中,类的私有属性和公有属性上都可以有属性(变量)

1
Human.prototype.race = '人族';

而在ES6中,公有属性上只能放方法(函数),如果要放属性(变量),非常麻烦,要用get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//定义父类
class Human{
constructor(name){
//私有属性
this.name = name;
};
//公有属性/方法
//属性
get race(){
return '人族';
};
//方法
say(){
console.log("我叫"+this.name);
};
}

-------------本文结束感谢您的阅读-------------