39

JavaScript のプロトタイプ ベースのオブジェクト指向プログラミング スタイルは興味深いものですが、クラスからオブジェクトを作成する機能が必要になる状況は数多くあります。

たとえば、ベクトル描画アプリケーションでは、通常、描画の開始時にワークスペースは空です。既存の線から新しい「線」を作成できません。より一般的には、オブジェクトが動的に作成されるすべての状況で、クラスを使用する必要があります。

私は多くのチュートリアルと「Javascript : 良い部分」という本を読みましたが、1) カプセル化と 2) 効率的なメンバー メソッドの宣言 (つまり: memberメソッドは一度定義され、すべてのクラス インスタンス間で共有されます)。

プライベート変数を定義するために、クロージャが使用されています:

function ClassA()
{
    var value = 1;
    this.getValue = function()
    {
        return value;
    }
}

ここでの問題は、"ClassA" のすべてのインスタンスがメンバー関数 "getValue" の独自のコピーを持つことであり、効率的ではありません。

メンバー関数を効率的に定義するために、プロトタイプが使用されています。

function ClassB()
{
    this.value = 1;
}

ClassB.prototype.getValue = function()
{
    return this.value;
}

ここでの問題は、メンバー変数「値」が public であることです。

「プライベート」変数はオブジェクトの作成中に定義する必要があるため(オブジェクトがそれらの値を公開せずに作成のコンテキストにアクセスできるようにするため)、プロトタイプベースのメンバー関数は簡単に解決できるとは思わないプロトタイプが意味をなすように、オブジェクトの作成後に定義を行う必要があります(「this.prototype」は存在しません、私は確認しました)。

または、何か不足していますか?


編集 :

まず、興味深い回答をありがとうございます。

最初のメッセージに少し精度を追加したかっただけです。

私が本当にやりたいことは、1) プライベート変数 (必要なものにしかアクセスできないため、カプセル化は適切です) と 2) 効率的なメンバー メソッドの宣言 (コピーを避ける) を持つことです。

単純なプライベート変数の宣言は、実際には javascript のクロージャーを介してのみ実現できるようです。これが、本質的に、クラス ベースのアプローチに焦点を当てた理由です。プロトタイプ ベースのアプローチで単純なプライベート変数宣言を実現する方法があれば、それで問題ありません。私は、クラス ベースのアプローチを熱心に支持しているわけではありません。

答えを読んだ後、単純な解決策は、プライベートを忘れて、特別なコーディング規則を使用して、他のプログラマーが「プライベート」変数に直接アクセスするのを防ぐことです...

そして、私がここで議論したかった問題に関して、私のタイトル/最初の文が誤解を招くものであったことに同意します.

4

8 に答える 8

28

あなたが StackOverflow のかなり新しいメンバーのように見えるので落胆させたくはありませんが、少し顔を合わせて、JavaScript で古典的な継承を実装しようとするのは本当に悪い考えだと言わなければなりません。 .

注: JavaScript で古典的な継承を実装するのは悪い考えだと言うとき、JavaScript で実際のクラス、インターフェイス、アクセス修飾子などをシミュレートしようとするのは悪い考えだということです。それにもかかわらず、JavaScript の設計パターンとしての古典的な継承は、プロトタイプの継承 (最大最小クラスなど) の構文糖衣にすぎないため、便利です。私はコードでこのデザイン パターンを常に使用しています ( a la Augment )。

JavaScript は、プロトタイプのオブジェクト指向プログラミング言語です。古典的なオブジェクト指向プログラミング言語ではありません。確かに、 JavaScript の上に従来の継承を実装することはできますが、その前に次のことを念頭に置いてください。

  1. あなたは言語の精神に逆らっています。つまり、問題に直面することになります。多くの問題 - パフォーマンス、可読性、保守性など。
  2. クラスは必要ありません。トーマス、あなたはクラスが必要だと本当に信じていることを知っていますが、これについては私を信頼してください. あなたはそうしない。

念のため、この質問に対して 2 つの回答を提供します。最初のものは、JavaScript で古典的な継承を行う方法を示します。2 つ目 (これをお勧めします) は、プロトタイプの継承を受け入れることを教えてくれます。

JavaScript における古典的な継承

ほとんどのプログラマーは、JavaScript で古典的な継承を実装しようとすることから始めます。Douglas Crockford のような JavaScript の達人でさえ、JavaScript で古典的な継承を実装しようとしました。私も JavaScript で古典的な継承を実装しようとしました。

まず、 Clockworkというライブラリを作成してから、augmentを作成しました。ただし、JavaScript の精神に反するため、これらのライブラリを使用することはお勧めしません。実は、これらの古典的な継承ライブラリを作成したとき、私はまだアマチュアの JavaScript プログラマーでした。

私がこれに言及する唯一の理由は、ある時点で誰もがアマチュアであるからです。JavaScript で古典的な継承パターンを使用しないことを望んでいますが、プロトタイプの継承がなぜ重要なのかを理解することはまだ期待できません。

何度か転ぶことなく自転車に乗る方法を学ぶことはできません。プロトタイプの継承に関しては、まだ学習段階にあると思います。古典的な継承の必要性は、自転車の補助輪のようなものです。

それでも、補助輪は重要です。必要に応じて、JavaScript でコードをより快適に記述できるようにする従来の継承ライブラリがいくつかあります。そのようなライブラリの 1 つがjTypesです。JavaScript プログラマーとしてのスキルに自信がある場合は、忘れずに補助輪を外してください。

注:個人的には、jTypes は少し好きではありません。

JavaScript におけるプロトタイプの継承

このセクションは、真のプロトタイプ継承について学ぶ準備ができたときに後で戻ってきて、次に何をすべきかを理解できるようにするためのマイルストーンとして書いています。

まず、次の行が間違っています。

JavaScript のプロトタイプ ベースのオブジェクト指向プログラミング スタイルは興味深いものですが、クラスからオブジェクトを作成する機能が必要になる状況は数多くあります。

これは次の理由で間違っています。

  1. JavaScript でクラスからオブジェクトを作成する必要はありません。
  2. JavaScript でクラスを作成する方法はありません。

はい、JavaScript で従来の継承をシミュレートすることは可能です。ただし、クラスではなくオブジェクトからプロパティを継承しています。たとえば、ECMAScript Harmony クラスは、プロトタイプ継承の古典的なパターンの構文糖衣にすぎません。

同じコンテキストで、あなたが示した例も間違っています:

たとえば、ベクトル描画アプリケーションでは、通常、描画の開始時にワークスペースは空です。既存の線から新しい「線」を作成できません。より一般的には、オブジェクトが動的に作成されるすべての状況で、クラスを使用する必要があります。

はい、最初はワークスペースが空であっても、既存の行から新しい行を作成できます。理解する必要があるのは、実際には線が描かれていないということです。

var line = {
    x1: 0,
    y1: 0,
    x2: 0,
    y2: 0,
    draw: function () {
        // drawing logic
    },
    create: function (x1, y1, x2, y2) {
        var line = Object.create(this);
        line.x1 = x1;
        line.y1 = y1;
        line.x2 = x2;
        line.y2 = y2;
        return line;
    }
};

これで、上記の行を呼び出すだけで描画line.drawできます。そうでなければ、そこから新しい行を作成できます。

var line2 = line.create(0, 0, 0, 100);
var line3 = line.create(0, 100, 100, 100);
var line4 = line.create(100, 100, 100, 0);
var line5 = line.create(100, 0, 0, 0);

line2.draw();
line3.draw();
line4.draw();
line5.draw();

line2line3line4およびは、描画すると正方形をline5形成します。100x100

結論

これで、JavaScript のクラスは本当に必要ないことがわかります。オブジェクトで十分です。カプセル化は、関数を使用して簡単に実現できます。

そうは言っても、各インスタンスのパブリック関数がオブジェクトのプライベート状態にアクセスすることはできません。各インスタンスには独自のパブリック関数のセットが必要です。

ただし、これは問題ではありません。理由は次のとおりです。

  1. プライベートステートは本当に必要ありません。あなたはそう思うかもしれませんが、実際にはそうではありません。
  2. 変数を本当にプライベートにしたい場合は、ThiefMasterが述べたように、変数名の前にアンダースコアを付けて、ユーザーにそれをいじらないように伝えてください。
于 2013-06-09T09:50:53.283 に答える
2

実行時にプライベート/パブリックは必要ありません。これらは静的に適用できます。プライベート プロパティが外部で使用されないようにするのに十分なほど複雑なプロジェクトには、ビルド/前処理ステップがあり、これを使用して事実を確認できます。private/public の構文を持つ言語でさえ、実行時に private にアクセスする方法があります。

クラスベースのオブジェクトの定義に関しては、使用しているコンストラクター + プロトタイプが最も簡単で効率的な方法です。あらゆる種類の追加の魔法は、より複雑になり、パフォーマンスが低下します。

キャッシュできるので、常にprototype繰り返す必要はありません。ClassB.prototype.

//in node.js you can leave the wrapper function out
var ClassB = (function() {
    var method = ClassB.prototype;

    function ClassB( value ) {
        this._value = value;
    }

    method.getValue = function() {
        return this._value;
    };

    method.setValue = function( value ) {
        this._value = value;
    };

    return ClassB;
})();

上記はライブラリを必要とせず、マクロを簡単に作成できます。

また、この場合、「プライベート」プロパティが正しく使用されていることを確認するには、正規表現でも十分です。ファイルを実行/([a-zA-Z$_-][a-zA-Z0-9$_-]*)\._.+/gして、最初の一致が常に であることを確認しますthishttp://jsfiddle.net/7gumy/

于 2013-06-09T12:47:40.770 に答える
1

あなたの質問のこの声明に関して:

たとえば、ベクトル描画アプリケーションでは、通常、描画の開始時にワークスペースは空です。既存の線から新しい「線」を作成できません。より一般的には、オブジェクトが動的に作成されるすべての状況で、クラスを使用する必要があります。

Javascript のオブジェクトは、既存のオブジェクトのクローンを作成することによってのみ作成できるという誤解を受けているようです。これは、「わかりましたが、最初のオブジェクトはどうですか?既存のオブジェクトのクローンを作成することによって作成することはできません。既存のオブジェクトではありません。」

ただし、オブジェクトをゼロから作成することもできます。その構文は次のように単純です。var object = {}

つまり、これは可能な限り単純なオブジェクトであり、空のオブジェクトです。もちろん、もっと便利なのは、その中に何かがあるオブジェクトです:

var object = {
  name: "Thing",
  value: 0,
  method: function() {
    return true;
  }
}

などなど、そして今、あなたはレースに出かけています!

于 2013-06-09T14:39:07.360 に答える