一、构造函数、实例
在JavaScript中,new关键字可以让一个函数变得与众不同,如下:
这时我们称Person为一个构造函数,p1是构造函数Person的实例。
二、原型
在JavaScript中,每个函数都有一个prototype属性,这个属性指向函数的原型对象。
当我们在创建对象时,可以根据自己的需求,选择性的将一些属性和方法通过prototype属性,挂载在原型对象上。每一个new出来的实例,都有一个__proto__属性,该属性指向构造函数的原型对象。通过这个属性,让实例对象也能够访问原型对象上的方法。因此,当所有的实例都能够通过__proto__访问到原型对象时,原型对象的方法与属性就变成了共有方法与属性。
而每个原型都有一个constructor属性,指向该关联的构造函数。
当我们访问实例对象中的属性或者方法时,会优先访问实例对象自身的属性和方法,如果实例对象自身没有对应的属性和方法,则访问其原型或者原型的原型的对象和方法(向上查找)。
三、原型链
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。
例如:p1是Person的实例,Person是Function对象的实例。而Function的原型对象同时又是Object的实例。
四、继承
原型链继承:
将父类的实例作为子类的原型
缺点:
引用类型的属性被所有实例共享,每个实例都有相同的属性,属性的继承无意义
在创建Child 的实例时, 不能向Person传参
构造函数继承:
在子类的内部调用父类,通过call改变父类中this的指向,等于是复制父类的实例属性给子类
优点:
可以实现多继承,避免了引用类型的属性被所有实例共享
创建子类实例时,可以向父类传递参数。可以在Child中向Parent传参
缺点:
实例只是子类的实例,不是父类的实例
无法继承原型中的方法
组合继承
使用原型链实现对原型方法的继承,而通过借用构造函数来实现对实例属性的继承。是JavaScript中最常用的继承模式
优点:
实例既是子类的实例,也是父类的实例
可以向父类传参
可以继承原型中的方法
缺点:调用了两次父类构造函数(组合继承最大的问题是无论什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部)