还是《Professional JavaScript for Web Developers》。
JavaScript也可以对象继承?当我看到这一章第一个反应便是这个,以前从来没有想过的,呵呵。
JS实现继承有如下几种办法:
1. 对象冒充
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function() {
alert(this.color);
};
}
function ClassB(sColor, sName) {
this.newMethod = ClassA;
this.newMethod(sColor);
delete this.newMethod;
this.name = sName;
this.sayName = function() {
alert(this.name);
};
}
var objA = new ClassA("red");
var objB = new ClassB("blue", "Nicholas");
objA.sayColor(); //outputs "red"
objB.sayColor(); //outputs "blue"
objB.sayName(); //outputs "Nicholas"
呵呵,是不是很有趣。注意黄色代码,所有新的属性和新的方法必须在删除了newMethod的代码行后定义。否则,可能会覆盖超类的相关属性和方法。
然后。。。用这个方法,还可以实现多重继承,哈哈哈
function ClassZ() {
this.newMethod = ClassX;
this.newMethod();
delete this.newMethod;
this.newMethod = ClassY;
this.newMethod();
delete this.newMethod;
}
不过这里有个小弊端,就是如果ClassX和ClassY有同名的属性和方法的话,ClassY具有优先级,使用时要注意点,呵呵。
2. call()方法
先看看call()方法的使用:
function sayColor(sPrefix, sSuffix) {
alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "red";
sayColor.call(obj, "The color is ", ", a very nice color indeed.");
这个例子中,sayColor虽然在对象外定义,即使他不属于任何对象,也可以引用关键字this。调用call()方法时,第一个参数是obj,说明应该赋予sayColor()函数中的参数的this关键字值是obj,第二个和第三个参数就是sayColor()函数本身的参数sPrefix和sSuffix
要与冒充对象方法一起使用该方法,只需要将前三行的赋值、调用和删除代码替换即可:
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function() {
alert(this.color);
};
}
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(sColor);
//delete this.newMethod;
ClassA.call(this, sColor);
this.name = sName;
this.sayName = function() {
alert(this.name);
};
}
var objA = new ClassA("red");
var objB = new ClassB("blue", "Nicholas");
objA.sayColor(); //outputs "red"
objB.sayColor(); //outputs "blue"
objB.sayName(); //outputs "Nicholas"
3. apply()方法
apply()方法和call()方法很相似,唯一不同的就是将call()方法后面带的多个参数存入数组再进行传递:
function sayColor(sPrefix, sSuffix) {
alert(sPrefix + this.color + sSuffix);
};
var obj = new Object();
obj.color = "red";
sayColor.apply(obj, new Array("The color is ", ", a very nice color indeed."));
自然,很容易得出用apply()方法实现继承的代码:
function ClassA(sColor) {
this.color = sColor;
this.sayColor = function() {
alert(this.color);
};
}
function ClassB(sColor, sName) {
//this.newMethod = ClassA;
//this.newMethod(sColor);
//delete this.newMethod;
ClassA.apply(this, new Array(sColor));
this.name = sName;
this.sayName = function() {
alert(this.name);
};
}
var objA = new ClassA("red");
var objB = new ClassB("blue", "Nicholas");
objA.sayColor(); //outputs "red"
objB.sayColor(); //outputs "blue"
objB.sayName(); //outputs "Nicholas"
其中,如果超类参数顺序与子类相同,图中黄色区域可以这么写:
ClassA.apply(this, arguments);
4. 原型链
function ClassA() {
}
ClassA.prototype.color = "red";
ClassA.prototype.sayColor = function() {
alert(this.color);
};
function ClassB() {
}
ClassB.prototype = new ClassA();
ClassB.prototype.name = "Nichloas";
ClassB.prototype.sayName = function() {
alert(this.name);
}
这个。。黄色那一句还是比较神奇的,呵呵。
不过要注意,调用ClassA的构造函数时,没有给它传递参数,这在原型链中是标准做法,要确保构造函数没有任何参数!
同时要注意上面的代码,和对象冒充类似,子类的所有的属性和方法都必须出现在prototype属性被赋值后,因为在它之前赋值的所有方法都会被删除!
原型链的好处在于可以使用类似如下代码检测:
var objB = new ClassB();
alert(objB instanceof ClassA) //outputs "true"
alert(objB instance of ClassB) //outputs "true"
而它的坏处在于不能多重继承,不过用惯Java的人应该比较习惯吧,呵呵。
5. 混合方式
总结以上几种方式,对象冒充的问题是必须使用构造函数方式,这不是最好的选择(参考我的另一片读书笔记《读书笔记之JavaScript的类编写方法》)。但如果使用原型链,又无法使用带参数的构造函数了。那么混合模式就是解决这两个问题的最好答案了,呵呵:
function ClassA(sColor) {
this.color = sColor;
}
ClassA.prototype.sayColor = function() {
alert(this.color);
};
function ClassB(sColor, sName) {
ClassA.call(this, sColor);
this.name = sName;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayName = function() {
alert(this.name);
};
这种方式是推荐使用的,呵呵。
最后还是那句话,以上源代码均来自Nicholas C. Zakas的《Professional JavaScript for Web Developers》