2013-03-26 118 views
1

這是情況;我有一個稱爲Car的基類(函數)和一些特定的子類,如truck,buspartybus用原型擴展基礎實例?

我的腳本讓用戶創建一個car,稍後由服務器隨機更新該車的隨機數(不可能預測它將會是什麼)。那時我已經有car與業主/ milage信息的實例,但需要與truck擴展創建的實例,這樣的方法getTopSpeed無效,我carInstance成爲myExtendedInstance instanceof truck===true

基類和子類功能的創建/延伸不是問題。這是代碼;

function Car(){ 
} 
Car.prototype.getTopSpeed=function(){ 
    return 100; 
} 

function Truck(){ 
    Truck.uber.constructor.call(this); 
} 
Truck.prototype.getTopSpeed=function(){ 
    return 20; 
} 
inherit.call(this, Truck, Car); 

繼承人由達斯汀·迪亞茲/羅斯Harmes

inherit=function _inherit(subClass, superClass){ 
    var F=function(){}; 
    F.prototype=superClass.prototype; 
    subClass.prototype=new F(); 
    subClass.prototype.constructor=subClass; 

    subClass.uber=superClass.prototype; 
    if (superClass.prototype.constructor===Object.prototype.constructor){ 
     superClass.prototype.constructor=superClass; 
    } 
} 

的問題是如何從已經創建的實例基類的去工作的子類和最佳回基類和繼承功能到另一個子類,而不需要創建一個新的子類實例並銷燬baseClass的實例。

我可以撥打myExtendedInstance=inherit(carInstance, Truck)嗎?這不會造成內存泄漏或模糊我的原型鏈嗎?如果不是,我怎麼能再次破壞原型鏈回到基本carInstance?

我想我的腦袋裏有一個結。任何幫助將非常感激! :)

+0

編輯並取消刪除我的回答,我第一次誤解了這個問題。 – C5H8NNaO4 2013-03-26 11:06:35

回答

1

難道我只是叫myExtendedInstance=inherit(carInstance, Truck)正確遷移實例?這不會造成內存泄漏或模糊我的原型鏈嗎?

不,它不會造成內存泄漏,但inherit只是希望構造函數(類)作爲參數沒有實例。

如果不是,我怎麼能再次破壞原型鏈返回基本carInstance?

您不能修改現有對象的原型鏈 - 除非使用非標準__proto__ property(在IE中不支持)。

取而代之,您將需要創建myExtendedInstance = new Truck;,然後複製舊的carInstance的所有者/微軟屬性。

+0

感謝您花時間回答!它是我所期望的。我會需要一種方法來替換另一個實例。它比複製屬性更復雜一點(我已經將它分解爲「模型」) - >問題更多地是關於更新其他結構所持有的引用。這就是爲什麼我想用另一個原型擴展實例。 Ohwell ..猜我會自己煮一些咖啡:) – japrescott 2013-03-26 10:10:14

1

你可以得到這個工作,但我認爲這種方法(改變一個對象的類)是複雜的。

我會創建一個Car類和另一個類CarSettings或類似的東西。

每個Car對象都包含一個CarSettings對象,它可以被修改或完全替換。

1

我繼承助手看到這樣的

那繼承的類被使用new並調用constructor無 參數subClass.prototype=new F(); 哪些可以(在我眼裏)輕鬆突破有點複雜造成的問題構造函數,在繼承過程中,如果構造函數依賴於特定的參數。

所以要解決這個問題,也能夠做你的問題是關於什麼的,我目前使用的是這樣的:

對不起,我誤解了最初的問題和改進我用一點點的幫助支持擴展和還原。

var base = (function baseConstructor() { 
    var obj = { 
     create: function instantiation(extend) { 
      var instance, args = [].slice.call(arguments); 
      if (this === base) { 
       throw new SyntaxError("You can't create instances of base"); 
      } else if (!this.hasOwnProperty("initclosure")) { 
       throw new SyntaxError("Cannot create instances without an constructor"); 
      } else if (this.singleton && this.instances.length !== 0) { 
       throw new SyntaxError("You can't create more than one Instance of a Singleton Class"); 
      } else { 
       if (!extend._class || !extend._class.isPrototypeOf(this)) { 
        instance = Object.create(this.pub); 
        this.instances.push(instance); 
       } else { 
        args = args.slice(1); 
        extend._class.remove(extend); 
        instance = this.extend(extend); 
       } 
       this.init.apply(instance, args); 
       instance.onCreate(); 
       return instance; 
      } 
     }, 
     extend: function (instance) { 
      if (!instance._class.isPrototypeOf(this)) { 
       return; 
      } 
      var extended = Object.create(this.pub); 
      for (var propInst in instance) { 
       if (instance.hasOwnProperty(propInst)) { 
        extended[propInst] = instance[propInst]; 
       } 
      } 
      instance._class.remove(instance); 
      this.instances.push(extended); 
      return extended; 
     }, 
     reduce: function (instance) { 
      if (!instance.instanceOf(this)) { 
       return; 
      } 
      var reduced = Object.create(this.pub); 
      for (var propRed in instance) { 
       if (instance.hasOwnProperty(propRed)) { 
        reduced[propRed] = instance[propRed]; 
       } 
      } 
      instance._class.remove(instance); 
      this.instances.push(reduced); 
      return reduced; 
     }, 
     remove: function (instance) { 
      if (instance.instanceOf(this)) { 
       var removed = this.instances.splice(this.instances.indexOf(instance), 1)[0]; 
       instance.onRemove(); 
       return removed; 
      } 
     }, 
     inherit: function inheritation(specsOpt) { 
      specsOpt = specsOpt || {}; 
      applyDefaults(specsOpt, { 
       singleton: false, 
       anonymous: false 
      }); 
      var sub = Object.create(this); 
      sub.pub = Object.create(this.pub); 
      sub.pub.proto = this.pub; 
      sub.pub._class = sub; 
      sub.instances = []; 
      sub.anonymous = specsOpt.anonymous; 
      sub.sup = this; 
      if (specsOpt.singleton) { 
       sub.singleton = specsOpt.singleton; 
       sub.getSingleton = getSingleton; 
       protect.call(sub, { 
        singleton: { 
         writable: false, 
         configurable: false, 
         enumerable: false 
        }, 
        getSingleton: { 
         writable: false, 
         configurable: false 
        } 
       }); 
      } 
      return sub; 
     }, 
     initclosure: function Base() {}, 
     instances: [], 
     pub: { 
      instanceOf: function (obj) { 
       if (!obj || !obj.pub) { 
        return this.className; 
       } 
       return obj.pub.isPrototypeOf(this); 
      }, 
      onRemove: function() {}, 
      onCreate: function() {}, 
      "_class": obj 
     } 
    }; 
    /* Helper Functions. --- Use function expressions instead of declarations to get JSHint/Lint strict mode violations 
    * 
    * TODO: Maybe add an obj.helper Propertie with usefull functions 
    */ 
    var applyDefaults = function (target, obj) { 
     for (var prop in obj) { 
      if (obj.hasOwnProperty(prop)) { 
       target[prop] = target[prop] || obj[prop]; 
      } 
     } 
    }; 
    var getSingleton = function() { //To get past the strict violation 
     return this.instances[0]; 
    }; 
    var protect = function (props, desc) { //Maybe change it a little 
     for (var prop in props) { 
      if (props.hasOwnProperty) { 
       Object.defineProperty(this, prop, props[prop] || desc); 
      } 
     } 
     return this; 
    }; 
    /* End Helpers 
    * 
    * Protecting 
    */ 
    Object.defineProperty(obj, "init", { 
     set: function (fn) { 
      if (typeof fn !== "function") { 
       throw new Error("Expected typeof init to be 'function'"); 
      } else if (Boolean(fn.name) === this.anonymous) { 
       try { 
        throw new Error("Expected the constructor " + (!this.anonymous ? "not " : "") + "to be Anonymous"); 
       } catch (e) { 
        console.error(e.stack); 
       } 
      } 
      if (!this.hasOwnProperty("initclosure")) { 
       this.initclosure = fn; 
       this.pub.constructor = this.init; 
       this.pub.className = fn.name; 
       protect.call(this.pub, { 
        constructor: false, 
        className: false 
       }, { 
        enumerable: false 
       }); 
      } 
     }, 
     get: function() { 
      var that = this; 
      var init = function init() { 
       if (that.pub.isPrototypeOf(this)) { 
        that.initclosure.apply(this, arguments); 
       } else { 
        throw new Error("init can't be called directly"); 
       } 
      }; 
      init.toString = function() { 
       return that.initclosure.toString(); 
      }; 
      return init; 
     } 
    }); 
    obj.toString = function() { 
     return "[class " + (this.initclosure.name || "Class") + "]"; 
    }; 
    obj.pub.toString = function() { 
     return "[instance " + (this.className || "Anonymous") + "]"; 
    }; 
    protect.call(obj, { 
     create: false, 
     inherit: false, 
     toString: false, 
     onRemove: { 
      enumerable: false 
     }, 
     onCreate: { 
      enumerable: false 
     }, 
     initclosure: { 
      enumerable: false 
     } 
    }, { 
     writable: false, 
     configurable: false 
    }); 
    protect.call(obj.pub, { 
     instanceOf: false, 
     toString: false, 
     "_class": { 
      enumerable: false 
     } 
    }, { 
     writable: false, 
     configurable: false, 
     enumerable: false 
    }); 
    return obj; 
})(); 

注:這依賴於Object.create這是ECMAScript中5推出,因此不被舊版本瀏覽器


支持鑑於繼承幫手,讓我們創建一些 「類」

var Car = base.inherit(); 
Car.pub.getTopSpeed = function() { 
    return 100; 
}; 
Car.init = function ClassCar(model) { 
    this.model = model || this.model || ""; 
}; 
Car.pub.type = "car"; 

既然我們有一個可繼承的superClass,讓Truck繼承自Car

var Truck = Car.inherit(); 
Truck.pub.getTopSpeed = function() { 
    return 20; 
}; 
Truck.pub.type = "truck"; 
Truck.init = function ClassTruck(model, color) { 
    Truck.sup.init.apply(this, [].slice.call(arguments)); 
    this.color = color; 
}; 

然後讓創造的Car

var myCar = Car.create("Porsche"); 
console.log(myCar.getTopSpeed(), myCar.className); //100, ClassCar 

一個實例現在,如果我的理解是否正確,你想的CarmyCar擴展現有的實例成爲Truck一個實例。
如果是這樣,讓我們​​做到這一點

var myExtendedTruck = Truck.extend(myCar); 
console.log(myExtendedTruck.getTopSpeed(), myExtendedTruck.className); //20, ClassTruck 
console.log(myExtendedTruck.instanceOf(Truck)); //true 

這僅僅設置了擴展的原型鏈copys實例變量到新的卡車實例。所以Car實例現在是Truck實例

或者,如果你想使用構造爲好。 create也適用於傳遞超類的實例。
然後它被擴展並初始化。

var myConstructedExtendedTruck = Truck.create(myCar, myCar.model, "Yellow"); 
console.log(myConstructedExtendedTruck.getTopSpeed(), myConstructedExtendedTruck.model, myConstructedExtendedTruck.color); //20 , Porsche , Yellow 

現在我們有一個擴展Car實例,也就是現在的Truck實例,被那個Trucks構造構成。

現在,如果我有這個權利,你希望能夠回到一個超類實例。

var myReducedCar = Car.reduce(myExtendedTruck); 
console.log(myReducedCar.getTopSpeed(), myReducedCar.className); //100, ClassCar 
console.log(myReducedCar.instanceOf(Truck)); //false 

這裏有一個Example on JSBin撥弄了一下週圍

編輯注:固定的代碼在類instances陣列

+0

謝謝你的擴展答案!有沒有一個模式名稱,或者你有更多的資源提供更深入的分析? – japrescott 2013-03-31 14:32:42

+0

不客氣=) 嗯,我真的不知道,如果這是一個模式名稱這樣的事情,我只是寫了一點以前沒有具體模式在腦海中,所以我害怕唯一可以提供給你的是回答有關代碼的任何進一步問題。 乾杯 – C5H8NNaO4 2013-04-02 13:06:07