记得以前写过一篇基于javascript function的类继承链实现。在ECMAScript3.1中,可以把function模拟成类。但是,因为原型链,构造体的实现非常繁琐。
现在各大浏览器升级到ECMAScript5之后,逐渐抛弃了以function来模拟类的思路。而转变为以对象来模拟类的思路。即:
var Cat = {};
以上的代码,可以看成是一个对象,也可以看成是一个类。当然像现在流行的静态语言中,比如java,Class本身就是对象。(究竟是先有对象还是先有类这样究竟是鸡生蛋还是蛋生鸡这样的哲学问题这里不加以讨论)
由此,新的Class函数如下:
var Class = (function() { /** * Initialze object from class. * @param class object. */ var initializeClass = (function() { if (Object.create) { return Object.create; } else { return function(o) { function F() {} F.prototype = o; return new F(); }; } })(); /** * The main function of Class. * * @param classContent * @param superClass */ return function() { var classPrototype = arguments[arguments.length - 1] || {}; for (var index = 0; index < arguments.length - 1; index++) { var superClass = arguments[index]; if (typeof superClass["initialize"] == "function") { classPrototype.superclass = superClass["initialize"]; } else { classPrototype.superclass = function() {}; } for (var prop in superClass) { if (prop == "initialize" || prop == "newInstance") { continue; } if (classPrototype.hasOwnProperty(prop)) { if (typeof superClass[prop] == "function") { classPrototype.superclass[prop] = superClass[prop]; } } else { classPrototype[prop] = superClass[prop]; } } } classPrototype.newInstance = function() { var instance = initializeClass(this); if (instance["initialize"]) { instance["initialize"].apply(instance, arguments); } return instance; }; return classPrototype; }; })();
大致功能为为对象增加getInstance方法和父类的superclass对象使其能够调用父类的构造体和方法。
测试:
var Animal = Class({ initialize: function(age) { this.age = age; }, eat: function() { alert("eat"); } }); var Cat = Class(Animal, { initialize: function(name, age) { // 调用父类构造体 Cat.superclass.call(this, age); // 本类属性 this.name = name; // 调用父类方法 this.superclass.eat(); }, eat: function() { alert("eat fish"); } }); var animal = Animal.newInstance(12); animal.eat(); var cat = Cat.newInstance("123", 12); alert(cat.name); alert(cat.age); cat.eat();
此方式注意点:
1.无法完全使用instanceof 来模糊判断是何种类型(判断父类)。但是可以用isPrototypeOf来精确判断是何种子类
2.无需使用关键字new来实例化对象。。而要用newInstance方法。(废话)
大家可以比较下我以前的那篇文章,会发现,继承的实现不是简单了一点半点。
至于instanceof的判断限制问题,大家完全可以不用担心
例如下面的java代码,instanceof用来判断何种接口,然后判断完后无非为了调用接口的方法。
public interface Animal {
}
public interface Cat extends Animal {
void eatFish();
}
public interface Dog extends Animal {
void eatFood();
}
public class Test {
public void animalExecute(Animal animal) {
if (animal instanceof Cat) {
((Cat) animal).eatFish();
} else if (animal instanceof Dog) {
((Cat) animal).eatFood();
}
}
}
而javascript在没有instanceof的情况下,代码可以这么实现
var Animal = Class({}); var Dog = Class(Animal, { eatFood: function() {}; }) var Cat = Class(Animal, { eatFish: function() {}; }) function animalExecute(animal) { if (animal.eatFood) {// Dog animal.eatFood(); } else if (animal.eatFish) {// Cat animal.eatFish(); } }
也就是说,javascript完全无需instanceof,也能完成相同功能
另外,在写组建的时候,通常都会用composite模式,在此模式下,instanceof就没有必要了。
为了解决instanceof的问题。我们引入function作为Object.create的模板,就能解决这个问题
var Class = (function() { /** * Inherits function.(node.js) * * @param ctor subclass's constructor. * @param superctor superclass's constructor. */ var inherits = function(ctor, superCtor) { ctor.super_ = superCtor; // ECMAScript 5 if (Object.create) { ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); } else { function F() {}; F.prototype = superCtor.prototype; ctor.prototype = new F(); ctor.prototype.constructor = ctor; } }; /** * Class function. */ return function() { var subClazz = arguments[arguments.length - 1] || function() {}; var fn = subClazz.initialize == null ? function() {} : subClazz.initialize; for (var index = 0; index < arguments.length - 1; index++) { inherits(fn, arguments[index]); } for (var prop in subClazz) { if (prop == "initialize") { continue; } fn.prototype[prop] = subClazz[prop]; } return fn; } })(); /** * The definition of Cat Class. */ var Cat = Class({ /** * Constructor. * * @param name Cat's name */ initialize: function(name) { this.name = name; }, /** * Eat function. */ eat: function() { alert(this.name + " is eating fish."); } }); /** * The definition of Black Cat Class. */ var BlackCat = Class(Cat, { /** * Constructor. * * @param name Cat's name. * @param age Cat's age. */ initialize: function(name, age) { // call the constructor of super class. BlackCat.super_.call(this, name); this.age = age; }, /** * Eat function. */ eat: function() { alert(this.name + "(" + this.age + ") is eating dog."); } }); /** * The definition of Black Fat Cat Class. */ var BlackFatCat = Class(BlackCat, { /** * Constructor. * * @param name Cat's name. * @param age Cat's age. * @param weight Cat's weight. */ initialize: function(name, age, weight) { // call the constructor of super class. BlackFatCat.super_.call(this, name, age); this.weight = weight; }, /** * Eat function. */ eat: function() { alert(this.name + "(" + this.age + ") is eating dog. My weight: " + this.weight); } }); /** * The definition of Dog Class. */ var Dog = Class({}); var cat = new BlackFatCat("John", 24, "100kg"); cat.eat(); // true alert(cat instanceof Cat); // true alert(cat instanceof BlackCat); // true alert(cat instanceof BlackFatCat); // true alert(cat.constructor === BlackFatCat); // false alert(cat instanceof Dog);
可以很明显的看到instanceof可以判断原型链上的function。