js的原型与继承一直是原生js最难理解的一部分,在vue中,this指针十分常用,而要弄清this指向,就要先弄清js的原型与继承。
原型
- js的对象分为普通对象和函数对象,每一个对象都有_proto_属性,但只有函数对象才有prototype属性(凡是通过New Function()创建的对象都是函数对象,其他的都是普通对象)。
- 函数对象的prototype属性指向创建它的构造函数的原型对象。
- 在默认情况下,所有的原型对象都会自动获得一个 constructor(构造函数)属性,这个属性(是一个指针)指向 prototype 属性所在的函数(Person)。
- 原型对象(Person.prototype)是 构造函数(Person)的一个实例
- 所有函数对象的proto都指向Function.prototype,它是一个空函数(Empty function)。
好处:让所有对象实例共享它所包含的属性和方法(可以跳出函数使用,不同函数需要很多相同属性时可使用)
继承
子类继承父类的特征和行为,使得子类具有父类的各种属性和方法。(JS中从严格意义上来说并没有类这一说法)。JS中的继承主要是靠原型来实现的。
- 原型继承
function Parent(){
this.name = '张三'
}
Parent.prototype.getAge = function(){
console.log(this.age,111)
}
function Child(){}
Child.prototype = new Parent()
var newChild = new Child()
newChild
缺点:父类的实例也同样可以调用子类的原型方法。
- 原型链继承
function Father(name,age){
this.name = name;
this.age = age;
}
Father.prototype.eat = function(){
console.log(this.name+"吃饭了");
}
function Son(){}
Son.prototype = new Father("李四",20);
var s1 = new Son(); // 创建子对象
s1
s1.eat();
缺点:只能继承父类原型上的方法,却无法继承父类上的属性
- 构造函数继承
function Parent(name){
this.name = name;
this.colors = ['red','green','pink'];
}
Parent.prototype.getName = function(){
console.log(this.name)
}
function Child(){
Parent.call(this,'Mary');//使用call()方法继承父类构造函数中的属性
this.age = '18';
}
var child1 = new Child();
child1
缺点:子类的实例只能继承父类的属性,却不能继承父类的原型的方法
- 组合继承(伪经典继承)(构造函数原型链)
function Father(name,age){
this.name=name;
this.age=age;
}
Father.prototype.eat=function(){ //原型的方法共享
console.log(this.name+"吃饭了");
}
function Child(name,age,sex){
Father.call(this,name,age)
this.sex=sex;
}
Child.prototype=new Father();
var child1=new Child("李四",20,"男");
child1.eat()
优点:既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性
缺点:父类被执行了两次,在使用 call 或 apply 继承属性时执行一次,在创建实例替换子类原型时又被执行了一次
-
原型式继承
核心:原型式继承的create方法本质上是对参数对象的一个浅拷贝
缺点:父类的引用属性会被所有子类实例共享。子类构建实例时不能向父类传递参数 -
寄生式继承