1、理解原型对象
无论什么时候,只要创建了一个新函数,就会为该函数创建一个prototype属性,这个属性就是函数的原型对象,该原型对象还有一个constructor属性和一个__proto__属性,由一个构造函数new出来的就是实例对象,它也有__proto__属性。他们之间的关系如下:
function Person(){};
var p=new Person();
Person.prototype==p.__proto__; //true
Person.prototype.constructor==Person; //true
总结:只有函数才有prototype属性,只有实例对象才有__proto__属性。
2、原型链
每个实例对象上会有一个__proto__属性,它是一个指针会指向上一层的原型对象,直到Object.prototype.__proto__ = null
,表示到达最顶端,这就形成了原型链。
var A=function(){};
var a=new A();
a.__proto__==A.prototype; //true
A.prototype.__proto__==Object.prototype; //true
Object.prototype.__proto__==null; true
3、继承
a.借助构造函数实现继承
function Parent(){
this.name='大明'
};
Parent.prototype.say=function(){
alert(this.name);
}
function Child(){
Parent.call(this); //this表示Child,继承Parent的属性和方法
this.type='child';
}
var p=new Child();
console.log(p.name); //大明
console.log(p.type); //child
p.say(); //TypeError
特点:p既有name属性,也有name属性,但是没有say方法。
缺点:构造函数只能继承父类构造函数里面的方法,无法继承父类原型对象上的方法。
b.借助原型链实现继承
function Parent(){
this.name='大明';
this.eat=[1,2,3]; //引用数据类型
};
Parent.prototype.say=function(){
alert(this.name);
}
function Child(){
this.type='child';
}
Child.prototype=new Parent();
var p=new Child();
console.log(p); //name、type属性和say方法都有
var p1=new Child();
var p2=new Child();
p1.name='小明';
console.log(p1.name,p2.name); //小明 大明
p2.eat.push(4);
console.log(p1.eat,p2.eat); //[1,2,3,4] [1,2,3,4]
特点:不仅可以继承父类上的方法,还可以继承父类原型上的方法。
缺点:当父类中包含引用类型
属性值时,由子类创建的多个实例中,只要其中一个实例引用属性只发生修改,其他实例的引用类型属性值也会立即发生改变。
c.组合方式继承
function Parent(){
this.name='大明';
this.eat=[1,2,3]; //引用数据类型
};
function Child(){
Parent.call(this); //构造函数继承
this.type='child';
}
Child.prototype=new Parent(); //原型链继承
var p1=new Child();
var p2=new Child();
p2.eat.push(4);
console.log(p1.eat,p2.eat); //[1,2,3] [1,2,3,4]
组合式继承也是实际开发中最常用的继承方式,解决了以上两种继承方式的问题。