web-dev-qa-db-ja.com

OO Javascriptコンストラクターパターン:新古典派と原型

私は Javascriptの良い部分についてのダグラス・クロックフォードの話 を見て、目を開けました。ある時点で彼は、「Javascriptは、優れたプログラマーが、習得しなくても効果的に使用できると信じている唯一の言語です」と述べました。それから私は気づきました、私はその男です。

その話の中で、彼は私にとってかなり驚くべき洞察に満ちた発言をしました。たとえば、JavaScriptは地球上で最も重要なプログラミング言語です。またはそれは地球上で最も人気のある言語です。そして、それは多くの深刻な方法で壊れていること。

彼が私にとって最も驚くべき発言は、「新しいものは危険だ」というものでした。彼はもうそれを使いません。彼はthisも使用していません。

彼は、プライベートメンバー変数とパブリックメンバー変数を許可し、newにもthisにも依存しない、Javascriptのコンストラクターの興味深いパターンを提示しました。次のようになります。

// neo-classical constructor
var container =  function(initialParam) {
    var instance = {}; // empty object 

    // private members
    var privateField_Value = 0;
    var privateField_Name = "default";

    var privateMethod_M1 = function (a,b,c) {
        // arbitrary
    }; 

    // initialParam is optional
    if (typeof initialParam !== "undefined") {
        privateField_Name= initialParam;
    }

    // public members
    instance.publicMethod = function(a, b, c) {
        // because of closures,
        // can call private methods or
        // access private fields here. 
    };

    instance.setValue = function(v) {
        privateField_Value = v;
    };

    instance.toString = function(){
        return "container(v='" + privateField_Value + "', n='" + privateField_Name + "')";
    };

    return instance;
}


// usage
var a = container("Wallaby");
WScript.echo(a.toString()); 
a.setValue(42);
WScript.echo(a.toString()); 

var b = container();
WScript.echo(b.toString()); 

[〜#〜] edit [〜#〜]:小文字のクラス名に切り替えるようにコードが更新されました。

このパターンは Crockfordの以前の使用モデル から進化しました。

質問:この種のコンストラクターパターンを使用していますか?あなたはそれが理解できると思いますか?もっと良いものはありますか?

43
Cheeso

これは、 モジュールパターン の非シングルトンバージョンのように見えます。これにより、JavaScriptの「クロージャ」を利用してプライベート変数をシミュレートできます。

私はそれが好きです(ちょっと...)。しかし、この方法で行われたプライベート変数の利点は実際にはわかりません。特に、(初期化後に)追加された新しいメソッドがプライベート変数にアクセスできないことを意味する場合はそうです。

さらに、JavaScriptのプロトタイプモデルを利用していません。すべてのメソッドとプロパティは、コンストラクターが呼び出されるたびに初期化する必要があります。これは、コンストラクターのプロトタイプにメソッドが格納されている場合は発生しません。実際、従来のコンストラクター/プロトタイプパターンを使用すると、はるかに高速になります。プライベート変数がパフォーマンスヒットを価値あるものにしていると本当に思いますか?

この種のモデルは、(疑似シングルトンを作成するために)一度だけ初期化されるため、モジュールパターンでは意味がありますが、ここで意味があるかどうかはわかりません。

この種のコンストラクターパターンを使用していますか?

いいえ、私はそのシングルトンバリアントを使用していますが、モジュールパターン...

あなたはそれが理解できると思いますか?

はい、それは読みやすく、非常に明確ですが、私はそのようなコンストラクター内にすべてをまとめるという考えは好きではありません。

もっと良いものはありますか?

本当にプライベート変数が必要な場合は、必ずそれを使用してください。それ以外の場合は、従来のコンストラクター/プロトタイプパターンを使用します(new/thisコンボに対するCrockfordの恐れを共有しない限り) :

function Constructor(foo) {
    this.foo = foo;
    // ...
}

Constructor.prototype.method = function() { };

このトピックに関するダグの見解に関連する他の同様の質問:

16
James

ほとんどの人が読みにくいと感じるので、私はこのパターンを避けます。私は一般的に2つのアプローチに従います:

  1. 何かが1つしかない場合は、匿名オブジェクトを使用します。

    var MyObject = {
        myMethod: function() {
            // do something
        }
    };
    
  2. 1つ以上の場合、標準のjavascriptプロトタイプ継承を使用します

    var MyClass = function() {
    };
    MyClass.prototype.myMethod = function() {
        // do something
    };
    
    var myObject = new MyClass();
    

(1)読み取り、理解、および書き込みがはるかに簡単です。 (2)複数のオブジェクトがある場合はより効率的です。 Crockfordのコードは、コンストラクター内に関数の新しいコピーを毎回作成します。クロージャには、デバッグがより困難になるという欠点もあります。

真にプライベートな変数は失われますが、慣例として、プライベートになるはずのメンバーの前に_を付けます。

thisはJavaScriptで明らかに難しい問題ですが、.call.applyを使用して適切に設定することで回避できます。また、var self = this;を使用して、メンバー関数内で定義された関数内でthisとして使用するクロージャ変数を作成することもよくあります。

MyClass.prototype.myMethod = function() {
    var self = this;

    // Either
    function inner1() {
        this.member();
    }
    inner1.call(this);

    // Or
    function inner2() {
        self.member();
    }
    inner2();
};
10
Caleb

この種のコンストラクターパターンを使用していますか?

いいえ、

あなたはそれが理解できると思いますか?

はい、非常に簡単です。

もっと良いものはありますか?

私はまだその話を見ていませんが、まもなく話に行きます。 それまでは、newthisを使用する危険性は見られません。理由は次のとおりです:

彼の指摘を聞いていないので、thisの性質と、特定のメソッドが実行されるコンテキストに応じて(直接)変化する傾向があるため、彼はそのようなものから離れることを提案していると推測できます。元のオブジェクト上またはコールバックとしてなど)。教育者として、彼は、言語の性質を最初に理解せずにJavaScriptに手を出す、ほとんど知らない経験の浅い開発者の人口統計のために、これらのキーワードの回避を教えている可能性があります。言語に精通している経験豊富な開発者にとって、言語のこの機能を回避する必要があるとは確信していません。これにより、驚くほどの柔軟性が得られます(withなどを回避することとはまったく異なります)。 )。そうは言っても、私は今それを見ています。

とにかく、automagic継承をサポートするある種のフレームワーク(dojo.declareなど)を使用しない場合、またはフレームワークに依存しないオブジェクトを作成する場合、現在、次のアプローチを採用しています。

定義:

var SomeObject = function() {
    /* Private access */
    var privateMember = "I am a private member";
    var privateMethod = bindScope(this, function() {
        console.log(privateMember, this.publicMember);
    });

    /* Public access */
    this.publicMember = "I am a public member";

    this.publicMethod = function() {
        console.log(privateMember, this.publicMember);
    };
    this.privateMethodWrapper = function() {
        privateMethod();
    }
};

使用法

var o = new SomeObject();
o.privateMethodWrapper();

ここで、bindScopeは、Dojoのdojo.hitchまたはPrototypeのFunction.prototype.bindに類似したユーティリティ関数です。

7
Justin Johnson

彼の本では、それは機能継承(52ページ)と呼ばれています。まだ使っていません。ダグラスからJavaScriptを学べば理解できます。これ以上のアプローチはないと思います。これは次の理由で優れています。

  • プログラマーがプライベートメンバーを作成できるようにします

  • プライベートメンバーを保護し、ふりプライバシーとは対照的にthisを排除します(プライベートメンバーは_で始まります)

  • 継承をスムーズにし、不要なコードを使用しません

ただし、いくつかの欠点があります。あなたはここでそれについてもっと読むことができます: http://www.bolinfest.com/javascript/inheritance.php

6
Anton Kuzmin

この種のコンストラクターパターンを使用していますか?

私が最初にJavaScriptを学び、ダグラス・クロックフォードの文献に出くわしたとき、私は以前にこのパターンを使用しました。

あなたはそれが理解できると思いますか?

クロージャを理解している限り、この方法は明らかです。

もっと良いものはありますか?

それはあなたが達成しようとしていることに依存します。可能な限りばかげた証拠となるライブラリを作成しようとしているのであれば、私はプライベート変数の使用を完全に理解できます。これは、ユーザーがオブジェクトの整合性を不注意に妨害するのを防ぐのに役立つ場合があります。はい、時間のコストはわずかに大きくなる可能性があります(約3倍)が、実際にアプリケーションに影響を与えるまで、時間のコストは議論の余地があります。以前の投稿で言及されたテスト( test )は、オブジェクトの関数にアクセスするのにかかる時間を考慮していないことに気づきました。

プロトタイプ/コンストラクターの方法では、通常、構築時間が短縮されることがわかりました。そうですが、必ずしも取得にかかる時間を節約できるとは限りません。プロトタイプチェーンを使用して関数を見つけるには、使用しているオブジェクトに関数を直接アタッチするのではなく、追加のコストがかかります。したがって、多くのプロトタイプ関数を呼び出していて、多くのオブジェクトを構築していない場合は、Crockfordのパターンを使用する方が理にかなっている可能性があります。

ただし、大勢のユーザー向けにコードを記述していない場合は、プライベート変数を使用したくない傾向があります。私がすべて有能なコーダーである人々のチームと一緒にいる場合、私が自分でコーディングしてコメントすれば、彼らが私のコードの使い方を知っていると一般的に信じることができます。それから私はCrockfordのパターンに似たものをプライベートメンバー/メソッドなしで使用します。

1
MillsJROSS

常に同じ種類のオブジェクトを作成する必要がある場合は、プロトタイプコンストラクターパターンを使用します。これは、自動委任経由プロトタイプ]を介してメソッドとプロパティを共有できるためです。チェーン

また、プライベートオブジェクトを保持することもできます。このアプローチを見てください:

var ConstructorName = (function() { //IIFE
    'use strict';

    function privateMethod (args) {}

    function ConstructorName(args) {
        // enforces new (prevent 'this' be the global scope)
        if (!(this instanceof ConstructorName)) {
            return new ConstructorName(args);
        }
        // constructor body
    }

    // shared members (automatic delegation)
    ConstructorName.prototype.methodName = function(args) {
        // use private objects
    };

    return ConstructorName;
}());

コンストラクター関数を作成する興味深い方法を見つけることができるこの回答を確認することをお勧めします:Javascriptオブジェクトを作成するさまざまな方法

これは、プロトタイプコンストラクターのアプローチを使用できる例です。JavaScriptでカスタムエラーを作成するにはどうすればよいですか?

1
jherax

ダグラス・クロックフォードが「高度なJavaScript」プレゼンテーションビデオで説明しているように、このパターンの要点をここで報告するのに役立つと思います。

重要な点は、新しい演算子の使用を避けることです。オブジェクトコンストラクター関数を呼び出すときに誰かがnew演算子の使用を忘れると、コンストラクターに追加されたオブジェクトメンバーはすべてグローバル名前空間になります。この場合、間違いを知らせる実行時の警告やエラーはなく、グローバル名前空間は単に汚染されることに注意してください。そして、これは深刻なセキュリティへの影響があり、診断が難しい多くのバグの原因であることが証明されています。

それがこのPowerconstructorパターンの要点です。

プライベート/特権部分はセカンダリです。別の方法でそれを行うことは問題ありません。つまり、プロトタイプメンバーは使用されません。

HTH luKa

0
luKa