1

これが状況です。と呼ばれる基本クラス(関数)と、、、などのCar特定のサブクラスがあります。truckbuspartybus

私のスクリプトでは、ユーザーがを作成し、car後でその車が正確にランダムであるものでサーバーによって更新されます(それが何になるかを予測することはできません)。その時点で、私はすでに所有者/マイレージ情報を持つインスタンスを持っていますが、メソッドがオーバーライドされて私のになるようにcar、作成されたインスタンスを拡張する必要がありますtruckgetTopSpeedcarInstancemyExtendedInstance 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);

ダスティン・ディアス/ロス・ハーメスによる継承関数はここにあります

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のインスタンスを破棄することなく、すでに作成されたbaseClassのインスタンスから動作中のサブクラスに、最適にbaseClassと別のサブクラスに戻る方法です。

電話してもmyExtendedInstance=inherit(carInstance, Truck)いいですか?それはメモリリークを引き起こしたり、私のプロトタイプチェーンを覆い隠したりしませんか?そうでない場合は、プロトタイプチェーンを再度破棄して、基本的なcarInstanceに戻すにはどうすればよいですか?

頭のどこかに結び目があると思います。どんな助けでも大歓迎です!:)

4

3 に答える 3

1

これを機能させることはできますが、このアプローチ(オブジェクトのクラスを変更する)は複雑だと思います。

Carクラスと別のクラスCarSettingsなどを作成します。

各Carオブジェクトには、変更または完全に置き換えることができるCarSettingsオブジェクトが含まれます。

于 2013-03-26T07:31:31.053 に答える
1

このような継承ヘルパーで私が見る問題は

継承されたクラスは、引数なしを使用newして呼び出すことで作成されます。 これは、コンストラクターが特定の引数に依存している場合、継承プロセス中に、 (私の目には) もう少し複雑なコンストラクターを簡単に壊す可能性があります。constructorsubClass.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";

継承可能なスーパークラスができたのでTruckCar

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

正しく理解できたら、の既存のインスタンスを拡張してのインスタンスmyCarCarしますTruck
もしそうなら、これをしましょう

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

これは、拡張されたプロトタイプチェーンを設定するだけで、インスタンス変数を新しいTruckインスタンスにコピーします。つまり、CarインスタンスはTruckインスタンスになります

または、コンストラクターも使用する場合。 createスーパークラスのインスタンスを渡すときにも機能します。
次に、拡張されて初期化されます。

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

これで、extendCarインスタンスができました。これは、コンストラクターのインスタンスでTruckあり、コンストラクターによって適切に構築されていTrucksます。

これが正しければ、スーパークラスのインスタンスにも戻れるようになります。

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

少しいじるJSBinの例を次に示します

編集メモ:Classesinstances配列内のインスタンスを適切に移動するようにコードを修正しました

于 2013-03-26T08:22:59.100 に答える
1

電話してもmyExtendedInstance=inherit(carInstance, Truck)いいですか?それはメモリリークを引き起こしたり、私のプロトタイプチェーンを覆い隠したりしませんか?

いいえ。メモリリークは発生しませんが、インスタンスではなく引数としてコンストラクター関数inherit(クラス)を想定しているだけです。

そうでない場合は、プロトタイプチェーンを再度破棄して、基本的なcarInstanceに戻すにはどうすればよいですか?

非標準の__proto__プロパティ(たとえばIEではサポートされていない)を使用しない限り、既存のオブジェクトのプロトタイプチェーンを変更することはできません。

myExtendedInstance = new Truck;代わりに、古いプロパティから所有者/マイレージプロパティを作成してコピーする必要がありますcarInstance

于 2013-03-26T09:46:18.437 に答える