2012-02-28 2 views
7

私はPythonとSmalltalkのバックグラウンドからJavascriptに来ています。 ECMAScript5では、新しいオペレータなしでプロトタイプオブジェクト指向に手を入れたかったのです。 。Object.createと名前付きコンストラクタを持つプロトタイプOO

制約:

  • プロトタイプチェーンのようなサポート
  • のalloc()のinit()作成シーケンスをデバッグWebInspectorためのinstanceofは
  • という名前のコンストラクタのために正しいものでなければならないクラスを作成するには

    • オプションのnew演算子Objective-CとPython

    ここで私はt彼の基準:

    function subclass(Class, Base) { 
        "use strict"; 
        function create(self, args) { 
         if (!(self instanceof this)) 
          self = Object.create(this.prototype); 
         var init = self.__init__; 
         return init ? init.apply(self, args) : self; 
        } 
        if (Base instanceof Function) Base = Base.prototype; 
        else if (Base===undefined) Base = Object.prototype; 
    
        Class.prototype = Object.create(Base); 
        Class.prototype.constructor = Class; 
        Class.create = create; 
    
        Class.define = function define(name, fn) { return Class.prototype[name] = fn; }; 
        Class.define('__name__', Class.name); 
        return Class; 
    } 
    

    そして、簡単なモックアップで動作するようです:ファクトリ関数としてのクラスを使用して

    function Family(){return Family.create(this, arguments)} 
    subclass(Family, Object); 
    Family.define('__init__', function __init__(n){this.name=n; return this;}); 
    
    function Tribe(){return Tribe.create(this, arguments)} 
    subclass(Tribe, Family); 
    function Genus(){return Genus.create(this, arguments)} 
    subclass(Genus, Tribe); 
    function Species(){return Species.create(this, arguments)} 
    subclass(Species, Genus); 
    

    var dog = Species('dog'); 
    console.assert(dog instanceof Object); 
    console.assert(dog instanceof Family); 
    console.assert(dog instanceof Tribe); 
    console.assert(dog instanceof Genus); 
    console.assert(dog instanceof Species); 
    

    またはnew演算子を使用して:

    var cat = new Species('cat'); 
    console.assert(cat instanceof Object); 
    console.assert(cat instanceof Family); 
    console.assert(cat instanceof Tribe); 
    console.assert(cat instanceof Genus); 
    console.assert(cat instanceof Species); 
    
    console.assert(Object.getPrototypeOf(dog) === Object.getPrototypeOf(cat)) 
    

    私は必要が見落とされましたか私の実装でプロトタイプオブジェクト指向の機能? Javascriptの規約や対話を変更する必要がありますか?要約すると、ここに「つまらない」とは何か、明らかな改善がなされていますか?

    私はコンストラクタ定義でDRYerになりたがっていましたが、関数の名前プロパティは書き込み可能ではなく、WebKit Inspectorのオブジェクト名をサポートしています。私は欲しいものを達成するための評価を作ることができましたが、... yuck。

  • +0

    うわー、私はジャバスクリプトをこの方法を使用して見たことがない...あなたは「信頼できない」件のデータを処理するとき場合 –

    +0

    evalのは危険ですが、私はあなたが[この操作を行う際に1が実際に、evalを使うべきではない理由を参照していけませんあなたはjsに連想配列がないので、ある種の評価を使用しています。 – mpm

    +1

    @camusはい、私は '/ \ w + /'正規表現を使って有効なメソッド名だけであることを確認しました。しかし、それはタックをインストールするためにスレッジハンマーを使用するようです - それは関数の名前プロパティを設定することが許可されていれば、はるかにエレガントになります。結局、Javascriptはプロトタイプ言語です。 –

    答えて

    8

    編集:ああ、今質問があります。答え:いいえ、あなたはそれを正確に得ました。関数の名前を設定する唯一の方法は、評価時に関数宣言を使用する場合です。したがって、あなたはソースコードにevalを入れる必要があります。以前より簡単な質問に答えましたが、同じ要点でMinor drawback with Crockford Prototypical Inheritanceと答えました。このトピックに関する別のリソースはhttp://kangax.github.com/nfe/

    です。機能の変更不可能な名前をデバッガの外観から離すために、displayNameプロパティを使用する動きがあります。これはFirefoxやその他のもので実装されていますが、es6に含まれていますが、まだ暫定仕様の一部ではありません。http://wiki.ecmascript.org/doku.php?id=strawman:name_property_of_functions

    ここでは、Chromeで名前を付ける関数http://querypoint-debugging.googlecode.com/files/NamingJSFunctions.pdf

    そして、ここでそれはまだ実装されていない理由は、クロムの問題が議論されています。オリジナルの答え上へ、http://code.google.com/p/chromium/issues/detail?id=17356

    あなたが達成するために着手した何を、あなたはそれをよくやりました。私がやった同様のタイプのもののカップルの例:

    まずあなたのようなものを行うことができるシンプルな「遺伝」関数である:

    var MyObjCtor = heritable({ 
        constructor: function MyObj(){ /* ctor stuff */}, 
        super: SomeCtor, 
        prop1: val, 
        prop2: val, 
        /**etc..*/ 
    }); 
    
    
    function heritable(definition){ 
        var ctor = definition.constructor; 
        Object.defineProperty(ctor, 'super', { 
        value: definition.super, 
        configurable: true, 
        writable: true 
        }); 
        ctor.prototype = Object.create(ctor.super.prototype); 
        delete definition.super; 
    
        Object.keys(definition).forEach(function(prop){ 
        var desc = Object.getOwnPropertyDescriptor(definition, prop); 
        desc.enumerable = false; 
        Object.defineProperty(ctor.prototype, prop, desc); 
        }); 
    
        function construct(){ 
        var obj = new (ctor.bind.apply(ctor, [].concat.apply([null], arguments))); 
        ctor.super.call(obj); 
        return obj; 
        } 
    
        construct.prototype = ctor.prototype; 
    
        return construct; 
    } 
    

    他はlibffiで使用する構造体を作成しています(ノード-ffi)。本質的には、コンストラクタ継承とプロトタイプ継承の両方があります。構造体のインスタンスを作成するコンストラクタを継承するコンストラクタを作成します。 https://github.com/Benvie/node-ffi-tools/blob/master/lib/Struct.js

    名前付き関数を作成するには、evalを使用する必要があります。名前付きの関数が必要な場合は、それが必要です。私はそれが必要な場所で使用することについて喜んでいません。

    +0

    リンク、コードサンプル、コードレビューは非常に有益です。ありがとうございます。 –

    4

    シェーン私はあなたのバックグラウンドから来ていることを伝えることができますJavascriptはあなたがやろうとしているものとは異なるやり方で動作します。その答えをすぐに嫌う前に...

    私はOOPを愛し、非常にOOPフレンドリーな背景から来ています。 Prototypeオブジェクトの周りに私の頭を包み込むために私はしばらく時間がかかります(プロトタイプメソッドを静的関数と考える...初期化されたオブジェクトでのみ動作します)。 javascriptで行われる多くのことは、良いセキュリティとカプセル化を使用する言語からの「間違った」ものだと感じています。

    「新しい」演算子を使用する唯一の理由は、複数のインスタンスを作成している場合や、イベントがトリガーされるまで何らかのタイプのロジックが実行されないようにしようとする場合です。

    私はこれが答えではないことを知っています...コメントを入力するのはあまりにも大きかったです。実際により良い視点を得るには、オブジェクトとヴァールを作成してDOMを表示する必要があります。これにより、文字通りどこからでもどこにでもアクセスできることがわかります。私はthese articles非常に細部を記入するために手抜きを見つけた。

    幸運のベスト。

    UPDATEあなたはここで私は私が聞いたことがあると思いたいいくつかのヒントがあり、同じオブジェクトの別のインスタンスを作成するためにファクトリメソッドが必要ですのであれば

    • てください、お願い、してくださいあなたが知っているOOカプセル化から学んだすべてのルールを忘れてしまいます。これは、protoオブジェクト、リテラルオブジェクト、およびインスタンスオブジェクトを一緒に使用すると、あなたに自然ではない方法でアクセスする方法が非常に多くなるからです。
    • 抽象化や継承のいずれかのタイプを使用する予定の場合は、DOMで遊ぶことを強くお勧めします。いくつかのオブジェクトを作成し、それらをDOMで探し、何が起こっているかを見ます。既存のプロトタイプを使用して継承をシミュレートすることができます(インタフェースと抽象クラスを持たない私を殺しました!)。
    • javascriptのすべてがオブジェクトであることを知っています。これは、非常にクールなもの(関数を返す、関数を返す、簡単なコールバックメソッドを作成する)を行うことを可能にします。 javascriptは '.call()'、 '.apply()'、および '.bind()'を呼び出します。

    これらの2つのことを知っていると、ソリューションの解決に役立ちます。あなたのDOMをきれいに保ち、良いjavascript開発の慣行に従うと、グローバル名前空間をきれいに保つことができます(DOMのウィンドウ参照)。これはまた、あなたがやっていることで「間違っている」と感じるすべてのことについて「より良い」気持ちになります。そして、一貫してください。

    これは私が試行錯誤して学んだことです。Javascriptは、古典的なオブジェクト指向言語との違いを頭の中で包むことができれば、素晴らしい独特の言語です。がんばろう!

    +0

    洞察に感謝します。私はクラス作成の新しいファクトリメソッドスタイルを必要とするが、それでもまだどこかのjavascriptの期待と互換性があるという特定のユースケースを念頭に置いています。 –

    +1

    @ShaneHollowayよかったよ。次に、あなたのオブジェクトを作成するために 'new'キーワードをdefで使うべきです。私は自分の答えを更新して、それがあなたに役立つかどうかを見てみましょう。 – jjNford

    +0

    素晴らしい経験とヒントをありがとう。私はプロトタイプのプログラミングが始めるにはちょっと心配していることに同意します。 –

    1

    evalなしで名前付きコンストラクタを作成することはできません。ライブラリが名前空間を実装している場合は、名前付きの隠しプロパティを持つコンストラクタだけが見つかります。

    残りの質問に答えると、多くのjavascriptクラスライブラリがあります。あなた自身で書くこともできますが、正しいやり方をしたい場合は、正しい継承システムを理解して実装するのがちょっと面倒です。

    私は最近、すべてのライブラリを改善しようとする自分のクラスライブラリを作成しました:Classful JS。私は、シンプルなデザイン、使い方、そしてサブクラスからスーパーメソッドを呼び出すような他のいくつかの改良があるので、見てみる価値はあると思います(私はこれを行うことができるライブラリは見ていません。オーバーライド)。

    3

    これはあなたの質問に答えることができないかもしれませんが、これはおそらくあなたが望むすべてのことをするわけではありませんが、これはOO JavaScriptに関する別の方法です。私は主に構文が好きです。私はgrokに簡単ですが、私はあなたの背景を持っていません。

    // ------- Base Animal class ----------------------- 
    var Animal = function(name) { 
        this.name = name; 
    }; 
    
    Animal.prototype.GetName = function() { 
        return this.name; 
    }; 
    // ------- End Base Animal class ----------------------- 
    
    // ------- Inherited Dog class ----------------------- 
    var Dog = function(name, breed) { 
        Animal.call(this, name); 
        this.breed = breed; 
    }; 
    
    Dog.prototype = new Animal(); 
    Dog.prototype.constructor = Dog; 
    
    Dog.prototype.GetBreed = function() { 
        return this.breed; 
    }; 
    
    Dog.prototype.Bark = function() { 
        alert(this.GetName() + ' the ' + this.GetBreed() + ' says "Arf!"'); 
    }; 
    // ------- End Inherited Dog class ----------------------- 
    
    // Usage 
    var harper = new Dog('Harper', 'miniature pinscher'); 
    harper.Bark(); 
    
    +1

    私は '.call'を使ってインスタンスを初期化するのが好きですが、これはきれいですが、なぜ大文字で始まるメソッド名を使うのですか?JavaScriptの命名規則に従って、すべての名前はコンストラクタ名といくつかのホストオブジェクト名 – Luc125

    +1

    ほとんどすべてがcamelCaseのところで見た命名規則が気に入らない。 「クラス」と「パブリックメソッド」をPascalCaseし、可読性を高めるためにcamelCaseを使用します。 – JoshNaro

    +0

    私はこのJavascript OOPイディオムをいくつかの場所で見てきました。これは基本的にヘルパーメソッドのないCrockfordのpseudoclassicalな例です。残念なことに、私のユースケースでは、 "クラス"が標準のJavascriptコンストラクタと同様にファクトリメソッドとして正しく動作する必要があります。 –

    関連する問題