深入剖析JavaScript之原型与继承

1.为什么要深入理解原型与继承

现如今前端框架多到数不胜数,类似angular,Vue,react三大框架大家都手到擒拿,可是照样子画轮子和自己真正会做轮子其实是两码事,而且这些框架底层都应用了很多JavaScript底层逻辑,要想会画轮子我们就得把底层知识学扎实。

2.对原型的理解

基本上每一个对象都有它的原型(没有原型的对象不多,Object.prototype没有)。通过对象直接量创建的对象都具有同一个原型对象。可以通过Object.prototype获取原型对象。通过关键字new和构造函数调用创建的对象的原型就是构造函数的prototype属性的值。

代码体现为:

1
2
3
4
5
6
7
8
9
10
11
12
13
//对象直接量
var person={
name:"foo"
}
Object.prototype
//New关键字
var person1=new Object()
Object.prototype
//构造函数
function PersonClass(){
this.name="bar"
}
PersonClass.prototype

3.继承

3.1 原型链继承

要求你必须有一个对象作另一个对象的基础。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//构造函数
function Animal(){
}
//原型
Animal.prototype.name="pig";
Animal.prototype.friends=["dog","cat"]
Animal.prototype.sayname=function(){
  console.log(this.name)
}
var pig=new Animal();
pig.sayname();//pig
var d=new Animal();
d.sayname();//pig
pig.name="bird";
pig.friends.push("fish")
pig.sayname();//bird
d.sayname();//pig
pig.friends;//["dog","cat","fish"]
d.friends;//["dog","cat","fish"]

代码总结:原型链继承缺点直接从代码层面上看可以很清晰的看到当修改其实一个实例,其他实例的引用类型都会改变,因为所有实例的friends属性都是Animal原型上面的一个属性,其根本都是用的同一个内存地址。
所以当用原型链继承的时候只要改变了引用类型,那么所有实例的这个属性值都会跟着变化。

3.2 构造函数继承

关于原型链继承导致的引用类型值得问题,构造函数完美的解决了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//构造函数
function SuperType(name){
this.colors = ["red", "blue", "green"];
this.name=name;
}
SuperType.prototype.sayname=function(){
console.log(this.name)
}
function SubType(name){
//继承了 SuperType
SuperType.call(this,name);
}
var instance1 = new SubType("foo");
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
alert(instance1.name); //foo
var instance2 = new SubType("bar");
alert(instance2.colors); //"red,blue,green"
alert(instance2.name); //bar
当调用原型方法时,会报错
instance1.sayname()//instance1.sayname is not a function

代码总结:当用构造函数继承的时候,我们只是调用了构造函数方法,因为用call,this指向发生改变,导致原型并没有被调用。

3.3 组合继承

实际应用的时候,大多数都是希望属性能够继承而事件自定义,这个时候组合继承的优势就体现出来了。例如每个人都有自己独特的名字和身份证id,而每个人的兴趣爱好都不一样,这个时候当我们创造一个人的时候,其名字和id都可以通过构造函数实现,而兴趣爱好则可以通过原型模式实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//原型与构造组合模式
function Person (name, id) {
this.name = name;
this.id= id;
}
Person.prototype = {
hobby: ['running','football'],
sayName: function () { alert(this.name); },
sayAge: function () { alert(this.age); }
};
var p1 = new Person('Jack', 20)
p1.hobby.push("writing")
//p1:'Jack',20; __proto__: ['running','football'],sayName,sayAge
var p2 = new Person('Mark', 18)
p2.hobby=["reading"]
//p2:'Mark',18;__proto__: ['running','football'],sayName,sayAge
console.log(p1,p2)