引言
假如一个对象的
原型指向的是另一个对象的实例,而这个实例也有自己的原型指向,如此层层递进,即为原型链。
- 所有对象都
默认继承了Object对象,如我们通常会用到的toString和valueOf函数。
function Person(){
this.name="zhangsan";
this.age=24;
}
//没自己指定原型索引指向时默认会有类似Person.prototype=Object的写法;
Person.prototype.sayName=function(){
console.log(this.name);
}
var person=new Person();
console.log(person.valueOf());
//打印结果
Person { name: 'zhangsan', age: 24 }
原型链图:
顺着原型链最顶层的原型对象一定是Object对象
A对象的原型指向B对象的实例,则A对象的实例会共享B对象的实例属性,不太符合继承的设计理念
function Person(){
this.name="zhangsan";
this.age=24;
}
Person.prototype.sayName=function(){
return this.name;
}
function Student(score,personNo){
this.score=score;
this.personNo=personNo;
}
Student.prototype=new Person();
var student1=new Student(25,1112);
//与引用类型直接修改不同
//不能直接用student1.name="lisi",这相当于直接创建了一个name值为lisi的实例变量,没对原型中的name进行修改
Object.getPrototypeOf(student1).name="lisi";
student1.personNo=6987
console.log("personNo为:"+student1.personNo);
var student2=new Student(25,1112);
console.log(student2.name);
console.log(student2.personNo);
//打印结果
personNo为:6987
lisi
1112
上面Person里面的属性是基本数据类型,但如果是引用数据类型如Array,属性的值通过方法修改时,不会创建新的实例变量
function Person(){
this.name="oooo";
this.frinds=["zhang","wang"]
}
Person.prototype.sayName=function(){
return this.name;
}
function Student(score,personNo){
this.score=score;
this.personNo=personNo;
}
Student.prototype=new Person();
var student1=new Student(25,1112);
//直接修改
student1.frinds.push("haha");
student1.personNo=6987
console.log("personNo为:"+student1.personNo);
var student2=new Student(25,1112);
student2.frinds.push("wwww");
console.log(student2.name);
console.log(student2.frinds.toString());
//打印结果
personNo为:6987
oooo
zhang,wang,haha,wwww
为了解决上述
问题,提出了不同的解决思路
借用构造函数
将SuperType当做一个函数调用,将它的属性用call函数与SubType绑定
function SuperType(){
this.colors=["red","blue"];
}
function SubTyPe(){
SuperType.call(this);
}
var instance1=new SubTyPe();
instance1.colors.push("black");
console.log(instance1.colors.toString());
var instance2=new SubTyPe();
instance2.colors.push("white");
console.log(instance2.colors.toString());
//打印结果
red,blue,black
red,blue,white
问题:方法都在构造函数内部定义,函数的复用就无从谈起,且超类型原型中的方法,对子类型而言不可见。
组合继承
子类型A的原型指向超类型B的实例C,同时在A的构造函数中调用B函数来覆盖A指向的原型C中的属性和方法,只让C的原型里面的属性和方法有效。
function SuperType(name){
this.name=name;
this.colors=['red','blue'];
}
SuperType.prototype.sayName=function(){
return this.name;
}
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}
SubType.prototype=new SuperType();
SubType.prototype.constructor=SubType;
SubType.prototype.sayAge=function(){
return this.age;
}
var instance1=new SubType("张三",25);
instance1.colors.push("black");
console.log("insatnce1 "+instance1.sayName());
var instance2=new SubType("李四",21);
instance2.colors.push("brown");
console.log(instance2.colors.toString());
//打印结果
insatnce1 张三
red,blue,brown
原型式继承
基于已有的对象创建一个新对象,还不必自定义对象类型
function object(o){
function F(){};
F.prototype=o;
return new F();
}
var person={
name:"lisi",
friend:["zhangsan","lisi"],
}
var copyPerson1=object(person);
copyPerson1.friend.push("wanger");
var copyPerson2=object(person);
copyPerson2.friend.push("mazi");
console.log(copyPerson2.friend.toString());
//打印结果
zhangsan,lisi,wanger,mazi
ES5通过新增Object.create()规范了原型式继承,可传入两个参数----->1.用作新对象原型的对象 2.为新对象定义额外属性的对象。------> 前者定义原型属性,后者定义实例属性.
var person={
name:"zhangsan",
age:25,
colors:["red","blue"],
}
var student1=Object.create(person,{socre:{enumerable:false,writable:true,value:25}});
student1.socre=35;
student1.colors.push("black");
console.log("student1的分数:"+student1.socre);
var student2=Object.create(person,{socre:{enumerable:false,writable:true,value:25}});
student2.socre=45;
student2.colors.push("brown");
console.log("student2的分数:"+student2.socre);
console.log("student2:"+student2.colors.toString());
//打印结果
student1的分数:35
student2的分数:45
student2:red,blue,black,brown
寄生式继承
利用原型式继承,并在构造函数内部对其进行加强,感觉就是Object.create()传两个参数的具体写法。
function Person(o){
function F(){}
F.prototype=o;
return new F();
}
function Student(original){
var person=Person(original);
person.sayName=function(){
console.log(this.name);
}
return person;
}
var object={
name:"zhangsan",
age:25,
}
var student=Student(object);
student.sayName();
//打印结果
zhangsan
寄生组合式继承
在组合式继承中,构造函数A继承构造函数B,将A的原型索引指向B的实例,在A中调用了B来覆盖B中的实例属性------>所以我们真正需要的是,将A的原型索引指向B的实例的原型,再调用B函数创建实例属性。
//超类型
function SuperType(name){
this.name=name,
this.colors=["red","blue"]
}
SuperType.prototype.sayName=function(){
console.log(this.name);
}
//子类型
function SubType(name,age){
SuperType.call(this,name);
this.age=age;
}
//原型式继承
function clone(o){
function F(){}
F.prototype=o;
return new F();
}
//copy超类型的原型
function inheritPrototype(superType,subType){
var object=clone(superType.prototype);
object.contructor=subType;
subType.prototype=object;
}
inheritPrototype(SuperType,SubType);
var subType=new SuperType("zhangsan",25);
subType.sayName();
//打印结果
zhangsan