太阳雨

痛并快乐着

BlogJava 首页 新随笔 联系 聚合 管理
  67 Posts :: 3 Stories :: 33 Comments :: 0 Trackbacks

还是《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》

posted on 2009-11-06 01:51 小虫旺福 阅读(187) 评论(0)  编辑  收藏 所属分类: Javascript相关

只有注册用户登录后才能发表评论。


网站导航: