构造函数、实例、原型之间的关系
每个函数都有一个prototype属性指向原型;原型内部有个constructor属性指向函数;实例有个_proto_
指向原型,因为浏览器差异,有的浏览器不对外开放此属性。用一幅图简单描述如下(自己画的先凑活看吧^_^)
function Person(){
}
var p1 = new Person();
console.log(Person==Person.prototype.constructor);//true
原型链
基于三者的关系,可以让子类的原型指向父类的实例,那么就能实现继承。
function Person(){
}
Person.prototype.sayName=function(){
console.log("person's sayName");
}
function Student(){
}
Student.prototype=new Person();
var s1 = new Student();
s1.sayName();
可以看到s1指向的原型就是Person实例,s1原型中含有constructor指向Person,还有_proto_
指向Person prototype,在执行s1.sayName
时,首先在Student prototype寻找sayName函数,因为没有所以继续寻找上一级原型,Person prototype中有sayName,所以执行此方法。
这种方法实现的继承有个缺点就是无法往父类构造函数传参,为了解决此问题代码改为
借用构造函数
function Person(){
this.colors=["red","blue","green"];
}
function Student(){
Person.call(this);//调用父类构造函数
}
var t1 = new Student();
t1.colors.push("black");
console.log(t1.colors);//[ 'red', 'blue', 'green', 'black' ]
var t2 = new Student();
console.log(t2.colors);//[ 'red', 'blue', 'green' ]
这个模式的优点是可以传参,缺点就是,方法如果定义在构造函数中则犯了重复创造的毛病,所以可以将函数定义在原型上。结合原型链模式和构造函数模式,推出了组合模式。
组合模式
组合模式的思路是利用原型链来继承原型的属性和方法,利用构造函数模式继承父类的属性。
function Person(name){
this.colors=["red","blue","green"];
this.name=name;
}
Person.prototype.sayName=function(){
console.log(this.name);
}
function Student(name,age){
Person.call(this,name);//调用父类构造函数
this.age=age;
}
Student.prototype=new Person();
Student.prototype.sayAge=function(){
console.log(this.age);
}
var t1 = new Student("jun",30);
t1.colors.push("black");
console.log(t1.colors);//[ 'red', 'blue', 'green', 'black' ]
t1.sayName();//jun
t1.sayAge();//30
var t2 = new Student("jordan",50);
console.log(t2.colors);//[ 'red', 'blue', 'green' ]
t2.sayName();//jordan
t2.sayAge();//50
组合模式既利用了原型模式也利用了构造函数模式,是目前最常用的继承模式。
原型式继承
原型式继承就是将子类原型指向父类实例的过程封装起来了,并且不用创建子类类型就可以实现继承,简化了代码;注意原型式继承就是实现了一个浅复制,如果有引用类型要注意。
function object(o){
function F(){
}
F.prototype=o;
return new F();
}
var person={
"name":"jordan",
"colors":["red","blue"]
};
var o1 = object(person);
o1.name="haha";
console.log(o1.name);//haha
console.log(o1.colors);//[ 'red', 'blue' ]
console.log(o1.hasOwnProperty("name"));//true,原型上的name
o1.colors.push("black");
var o2=object(person);
console.log(o2.name);
console.log(o2.hasOwnProperty("name"));//false,实例上的name
console.log(o2.colors)//[ 'red', 'blue', 'black' ]
寄生式继承
简单来说,就是封装了继承过程的代码,并做了增强,跟原型式继承差不多。缺点就是,增强函数无法复用。
function object(o){
function F(){
}
F.prototype=o;
return new F();
}
function createAnother(original){
var obj = object(o);
obj.someMethod=function(){
}
return obj;
}
寄生组合式继承
组合式继承最大的缺点是调用了两次超类型的构造方法,第一次是在Student.prototype=new Person();
这会在Student的原型中创建一次属性,第二次是在new Student()
这会在Student实例中又创建了一次属性。寄生组合式继承的思路是,第一次调用超类构造函数的目的是为了获取超类原型的副本,可以通过复制一份超类原型去掉这一次。
function object(o){
function F(){
}
F.prototype=o;
return new F();
}
function inheritPrototype(subType,superType){
var obj = object(superType.prototype);
obj.constructor=subType;
subType.prototype=obj;
}
function Person(name){
this.name=name;
}
Person.prototype.sayName=function(){
console.log(this.name);
}
function Student(name,age){
Person.call(this,name);
this.age=age;
}
inheritPrototype(Student,Person);
var s1 = new Student("jun",30);
s1.sayName();//jun
这种模式只需要调用一次超类构造函数,是最理想的继承模式。
参考
javascript高级程序设计(第二版)
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/20294.html