讲解前需要明白的几点
- Javascript继承:继承机制并没有明确规定,完全由开发者决定最合适的继承方式
- 类的原型对象上的所有属性和方法都会被子类拥有
- this关键字:谁调用这个属性和方法this就指谁
1. 对象冒充方式实现继承(Object Masquerading)
- 对象冒充实现单继承
function ClassA(sColor){
this.name="Lily"
this.color=sColor;
this.sayColor=function () {
console.log(this.color)
}
}
function ClassB(sColor,sName) {
//newMethod()之前定义同名属性和方法会被父类覆盖
// this.name=sName;
// this.sayColor=function () {
// alert(sColor);
// }
this.newMethod=ClassA;
//调用方法后,相当于给B类增加了sColor属性和sayColor方法
this.newMethod(sColor);
//newMethod()之后同名方法会覆盖父类
this.sayColor=function () {
alert(sColor);
}
// 从对象上删除父类构造函数
delete this.newMethod;
this.name=sName;
this.sayName=function () {
console.log(this.name);
}
}
var objA=new ClassA("blue");
var objB=new ClassB("red","John");
objA.sayColor(); // blue
objB.sayColor(); // red
objB.sayName(); // John
子类中定义的属性和方法如果在调用this.newMethod()这行代码之前,那么子类同名属性和方法会被父类覆盖,反之会覆盖父类同名属性和方法,理由很简单,原来的赋值被新值覆盖
- 对象冒充实现多继承
function ClassC(sColor,sName){
this.newMethod=ClassA;
this.newMethod(sColor);
delete this.newMethod;
this.newMethod=ClassB;
this.newMethod(sColor,sName);
delete this.newMethod;
}
var obj=new ClassC("green","ClassC");
obj.sayName(); //ClassC
obj.sayColor(); // green
可见ClassC同时拥有ClassA和ClassB的特性
ClassA的同名属性和方法会被ClassB覆盖.同样因为后定义覆盖了先定义
2. call()方法实现继承
function sayColor(sPrefix,sSuffix){
console.log(sPrefix+this.color+sSuffix);
}
var obj=new Object();
obj.color="blue";
//会调用sayColor方法
sayColor.call(obj,"The color is "," a very nice color indeed.");
//下面与对象冒充一起使用
function ClassB(sColor,sName) {
// this.newMethod=ClassA;
// this.newMethod(sColor);
// delete this.newMethod;
//call就是方法调用没啥特别地方,做了上面三条语句做的事
//调用构造函数
ClassA.call(this,sColor);
this.name=sName;
this.sayName=function () {
console.log(this.name);
}
}
3. apply()方法实现继承
function sayColor(sPrefix,sSuffix){
console.log(sPrefix+this.color+sSuffix);
}
var obj=new Object();
obj.color="blue";
// 普通的方法调用
sayColor.apply(obj,new Array("The color is ", " a very nice color indeed."));
function ClassB(sColor,sName) {
//this.newMethod = ClassA;
//this.newMethod(color);
//delete this.newMethod;
//调用构造函数
// ClassA.apply(this,arguments)
ClassA.apply(this,new Array(sColor))
this.name=sName;
this.sayName=function () {
console.log(this.name);
}
}
var obj=new ClassB("red","Bill");
// 如果特意将ClassB的参数顺序调整ClassA不同,这时就不能用arguments,该使用ClassA.apply(this,new Array(sColor)),感兴趣自己试验一下
// ob.sayColor(); //Bill
obj.sayColor();
总结:call()和apply()让继承变得更清爽,查看源码你会发现是Function的原型方法。
4. 原型继承
function object(o) {
function F() {}
F.prototype=o;
return new F();
}
var person = {
name:"EvanChen",
friends:["Shelby","Court","Van"]
};
var person1=object(person);
//先从person1对象查找name,没找到会去原型对象上查找
console.log(person1.name);
es5以后,规范了这种继承,创建对象使用Object.create()
//只传一个参数时
var person1=Object.create(person);
console.log(person1.name);
//传两个参数时
var prop={son:{age:10}};
var person1=Object.create(person,prop);
总结:Object.create传入的第一个参数属性会作为person1的原型属性。第二个参数属性会作为person1对象上的属性
5. 原型链继承
function ClassA() {}
ClassA.prototype.color="blue";
ClassA.prototype.sayColor=function () {
console.log(this.color);
}
function ClassB() {
}
ClassB.prototype=new ClassA();
var obj=new ClassB();
obj.sayColor(); //blue
console.log(obj.color); //blue
可见ClassB同时拥有了ClassA的属性可方法
下面为ClassB添加更多原型方法
...
ClassB.prototype=new ClassA();
//下面的属性和方法是在赋值ClassA为新原型之后添加的属性和方法
ClassB.prototype.name="";
ClassB.prototype.sayName=function () {
console.log(this.name);
}
var classB=new ClassB();
classB.color="red";
classB.name="John";
classB.sayColor(); //red
classB.sayName(); // John
特别指出:
- ClassB原型对象上添加的新的属性和方法,请务必添加在ClassB.prototype=new ClassA()之后。因为原型已经指向ClassA,旧原型对象被销毁
- 通过修改类的原型对象方式没办法实现多继承,因为对象的原型对象仅有一个
- 对象和对象的原型上有同名属性或方法,优先会使用对象本身,如果对象上无此属性,才会使用原型对象上同名属性和方法
6. 混合构造函数和原型链方式实现继承
function ClassA(sColor) {
this.color=sColor;
}
ClassA.prototype.sayColor=function () {
console.log(this.color);
}
function ClassB(sColor,sName) {
ClassA.call(this,sColor);
this.name=sName;
}
ClassB.prototype=new ClassA();
//为ClassB添加新原型方法
ClassB.prototype.sayName=function () {
console.log(this.name);
}
var classA=new ClassA("purple");
var classB=new ClassB("red","John");
classA.sayColor(); // purple
classB.sayColor(); // red
classB.sayName(); // John
混合继承结合了构造函数创建各个对象拥有各自一份属性和原型方法被所有对象共享的优点
怎么样简单吧,喜欢的话,帮忙点个赞O(∩_∩)O谢谢!