13

私は自分の仕事で使用するための小さなJSフレームワークを構築してきましたが、ダグラス・クロックフォードの典型的な遺伝形式を採用したいと思います。プロトタイプオブジェクトがどのように機能するかについての一般的な考え方は理解できたと思いますが、最も単純な例を超えてこのパターンを使用する方法が明確ではありません。

私はそれを理解できるところまで肉付けします。

(function () {

    'use strict';

    var Vehicles = {};

    Vehicles.Vehicle = function () {
        this.go = function () {
            //go forwards
        };

        this.stop = function () {
            //stop
        };
    };

    Vehicles.Airplane = Object.create(Vehicles.Vehicle());

}());

これで、Vehicles.Airplaneオブジェクトはgo()とstop()を実行できますが、もっと必要です。このオブジェクトにtakeOff()メソッドとland()メソッドを追加したいと思います。後で醜いドット表記を使用することができます。

Vehicles.Airplane.takeOff = function () {
    //take off stuff
}

しかし、特に多くのメソッドやプロパティを追加する場合は、それは間違っているようです。ここで尋ねられる質問は私のものと非常に似ているようですが、答えは私にはまったく当てはまりません。答えは、Object.createを使用する前にオブジェクトリテラルを作成する必要があり、そのオブジェクトリテラルをcreateメソッドに渡す必要があることを示しています。ただし、与えられたサンプルコードでは、新しいオブジェクトは現在何も継承していないように見えます。

私が望んでいるのは、次のような構文です。

Vehicles.Airplane = Object.create(Vehicles.Vehicle({
    this.takeOff = function () {
        //takeOff stuff
    };
    this.land = function () {
        //land stuff
    };
}));

もちろん、Vehicle.Vehicleにオブジェクトリテラルではなく関数を渡しているため、この構文はObject.createでひどく壊れることを知っています。それは重要なことではありません。事後にドット表記で一度に1つずつリストする必要なしに、別のプロパティから継承するオブジェクトに新しいプロパティをどのように構築する必要があるのか​​疑問に思っています。


編集:

ベルギ、このトピックについて苦悩した後、私はあなたが「古典的なパターン」と表現したものを本当に使いたいと思います。これが私の最初の刺し傷です(今では、仮説をモックアップするのではなく、実際のコードスニペットを使用しています-私のくだらないメソッドスタブを見ることができます):

CS.Button = function (o) {
    o = o || {};

    function init(self) {
        self.domNode = dce('a');
        self.text = o.text || '';
        self.displayType = 'inline-block';
        self.disabled = o.disabled || false;

        self.domNode.appendChild(ctn(self.text));
        if (o.handler) {
            self.addListener('click', function () {
                o.handler(self);
            });
        }
    }

    this.setText = function (newText) {
        if (this.domNode.firstChild) {
            this.domNode.removeChild(this.domNode.firstChild);
        }
        this.domNode.appendChild(ctn(newText));
    };

    init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
    constructor: {value: CS.Button, configurable: true}
});

CS.Displayable = function (o) { // o = CS Object
    o = o || {};

    var f = Object.create(new CS.Element(o));

    function init(self) {
        if (!self.domAnchor) {
            self.domAnchor = self.domNode;
        }
        if (self.renderTo) {
            self.renderTo.appendChild(self.domAnchor);
        }
    }

    //Public Methods
    this.addClass = function (newClass) {
        if (typeof newClass === 'string') {
            this.domNode.className += ' ' + newClass;
        }
    };
    this.addListener = function (event, func, capture) {
        if (this.domNode.addEventListener) {
            this.domNode.addEventListener(event, func, capture);
        } else if (this.domNode.attachEvent) {
            this.domNode.attachEvent('on' + event, func);
        }
    };
    this.blur = function () {
        this.domNode.blur();
    };

    this.disable = function () {
        this.disabled = true;
    };

    this.enable = function () {
        this.disabled = false;
    };

    this.focus = function () {
        this.domNode.focus();
    };

    this.getHeight = function () {
        return this.domNode.offsetHeight;
    };

    this.getWidth = function () {
        return this.domNode.offsetWidth;
    };

    this.hide = function () {
        this.domNode.style.display = 'none';
    };

    this.isDisabled = function () {
        return this.disabled;
    };

    this.removeClass = function (classToRemove) {
        var classArray = this.domNode.className.split(' ');
        classArray.splice(classArray.indexOf(classToRemove), 1);
        this.domNode.className = classArray.join(' ');
    };

    this.removeListener = function () {
        //Remove DOM element listener
    };

    this.show = function () {
        this.domNode.style.display = this.displayType;
    };

    init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
    constructor: {value: CS.Displayable, configurable: true}
});

はっきり言って、まだうまくいっていないと言うべきですが、ほとんどの場合、私が正しい方向に進んでいるかどうかについてあなたの意見をお願いします。例のコメントで「インスタンス固有のプロパティとメソッド」について言及しました。これは、this.setTextメソッドなどが間違って配置されており、プロトタイプチェーンの子孫アイテムで使用できないことを意味しますか?

また、使用すると、宣言の順序が重要になるようです(CS.Buttonが最初にリストされているため、CS.Displayable.prototypeにアクセスできません。また、CS.Displayableは、その時点では未定義です。 mそれを参照しようとしています)。それは私がただ手直して対処しなければならないものですか(OCDのアルファベット順ではなく、祖先の順序でコードに入れてください)、それとも私が見落としているものがありますか?

4

2 に答える 2

13
Vehicles.Airplane = Object.create(Vehicles.Vehicle());

その行は間違っています。使用したいようですnew Vehicles.Vehicle-コンストラクターを呼び出さないでnewください!

それでも、どのパターンを使用したいかはわかりません。2つが頭に浮かびます。

古典的なパターン

標準のJSと同じようにコンストラクター関数を使用しています。継承は、プロトタイプオブジェクトを相互に継承し、子インスタンスに親コンストラクターを適用することによって行われます。コードは次のようになります。

Vehicles.Vehicle = function () {
    // instance-specific properties and methods,
    // initialising
}
Vehicles.Vehicle.prototype.go = function () {
     //go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
    //stop
};

Vehicles.Airplane = function() {
    // Vehicles.Vehicle.apply(this, arguments);
    // not needed here as "Vehicle" is empty

    // maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
    constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property

Vehicles.Airplane.prototype.takeOff = function () {
   //take off stuff
};

// usage:
var airplane = new Vehicles.Airplace(params);

純粋なプロトタイプパターン

コンストラクター関数の代わりにプレーンオブジェクトを使用しています-初期化はありません。インスタンスを作成し、継承を設定するには、のみObject.createが使用されます。これは、プロトタイプオブジェクトと空のコンストラクターだけを持っているようなものです。instancofここでは機能しません。コードは次のようになります。

Vehicles.Vehicle = {
    go: function () {
         //go forwards
    },
    stop: function () {
         //stop
    }
}; // just an object literal

Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
   //take off stuff
};

// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me
于 2013-01-10T21:49:36.057 に答える
5

あなたはObject.create間違っていました。最初の引数はオブジェクトである必要があります(おそらくそれが、リテラルを渡すことを人々が提案した理由です)。

最初の例では、実際に合格していますundefined

Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
                                                       // return undefined

以下は機能しますが、あまりクロックフォードっぽくはありません。

Vehicles.Airplane = Object.create(new Vehicles.Vehicle());

Crockfordがそれを行うと私が信じる方法(または、少なくとも、不平を言うことはありません):

var Vehicles = {};

Vehicles.Vehicle = {
    go : function() {
        // go stuff
    },
    stop : function() {
        // go stuff
    }
};

Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
    takeOff : { 
        value : function() {
            // take-off stuff
        }
    },
    land : {
        value: function() {
            // land stuff
        }
    }
});

これVehicles.Vehicleは単なる文字であり、他のオブジェクトのプロトタイプとして使用されることに注意してください。を呼び出すと、プロトタイプとしてObject.create渡され、の独自のプロパティになります。ボーイングなどを作成したい場合は、プロトタイプとして渡して、もう一度呼び出すことができます。Vehicles.VehicletakeOfflandVehicles.AirplaneObject.createVehicles.Airplane

2番目のパラメーターとして渡された独自のプロパティは、プロパティ記述子の表現を含むオブジェクトにパックされます。外側のキーはプロパティ/メソッドの名前であり、それぞれが実際の実装を含む別のオブジェクトを指しますvalueenumerable;のような他のキーを含めることもできます。そうしないと、デフォルト値が使用されます。記述子の詳細については、MDNページObject.definePropertyのを参照してください。

于 2013-01-10T21:45:08.333 に答える