ECMAScript 5による継承チェーンの実現


以前、javascript functionによるクラス相続チェーンの実現を書いたことがあります.ECMAScript 3.1では、functionをクラスにシミュレートできます.しかし、プロトタイプチェーンのため、構造体の実現は非常に煩雑です.
現在、各ブラウザはECMAScript 5にアップグレードされてから、徐々にfunctionで模擬クラスの考えを捨てました.対象に変換して類の構想をなぞらえ似せます.すなわち、
 
 
var Cat = {};
 以上のコードは、オブジェクトとして見てもいいし、クラスとして見てもいいです.もちろん今流行している静的な言語の中で、たとえばjava、クラス自体が対象です.(相手が先か、それとも先にこのような鶏の卵なのか、それとも卵なのかという哲学的な問題はここでは議論しません.)
 
これにより、新しいクラスの関数は以下の通りです.
 
 
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;
		};
	})();
 
大まかな機能は、オブジェクトにget Instance方法と親類の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.instance ofを完全に使用できず、どのタイプかを判断する(親タイプを判断する).しかし、isProttypeOfで正確にどのような種類かを判断できます.
2.キーワードnewを用いてオブジェクトを実装する必要はありません.newInstanceの方法を使います.(無駄話)
 
以前の文章を比べてみると、継承の実現は簡単ではないことが分かります.
 
instance ofの判断制限については、全く心配しなくても大丈夫です.
例えば、次のjavaコードは、どのインターフェースを判断するためにinstance ofを使用していますか?
 
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はinstance ofがない場合、コードはこのように実現できます.
 
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はinstance ofを全く必要とせず、同じ機能を実現することができます.
 
また、セッティングを書く時は、常にcompossiteモードを使いますが、このモードではinstance ofは必要ありません.
 
instance ofの問題を解決するために.私たちはfunctionをObject.creatのテンプレートとして導入すれば、この問題を解決できます.
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);  
 
instance ofがはっきりと見えます.プロトタイプチェーンのfunctionを判断できます.