7

このパターンは、JavaScriptオブジェクトを定義するためによく見られます

function Person(name) {
    this.name = name;
}
Person.prototype.describe = function () {
    return "Person called "+this.name;
};

また、この記事では、プロトタイプオブジェクトにプロパティを直接追加することはアンチパターンと見なされると述べています。

「古典的なクラスベース」の言語から来ているので、メソッドとは別にプロパティを定義する必要があることは、JavaScriptではさらに正しく聞こえません。メソッドは関数値を持つプロパティである必要があります(私はここにいますか?)

誰かがこれを説明できるかどうか、あるいはこれらの状況を処理するためのより良い方法を提案できるかどうかを知りたいと思いました

4

6 に答える 6

7

通常のオブジェクト指向言語では、メンバー、メソッド、およびコンストラクターを記述するクラスの定義があります。

JSでは、「クラス」の定義(他の言語のように実際にはクラスではありません...疑似クラスという用語が使用されることもあります)はコンストラクター自体です。オブジェクトがによってパラメータ化されている場合は、次のnameように書くのが理にかなっています。

function Person(name) {
    this.name = name;
}

つまり、プロパティnameはコンストラクタで設定する必要があります。

もちろん、あなたは書くことができます

function Person(name) {
    this.name = name;
    this.describe = function() { ... };
}

そしてそれはあなたが期待するように動作します。

ただし、この場合、コンストラクターを呼び出すたびに、メソッドの個別のインスタンスを作成します。

一方、ここでは:

Person.prototype.describe = function () {
    return "Person called "+this.name;
};

メソッドを定義するのは1回だけです。のすべてのインスタンスは、へのポインタ(ほとんどのブラウザでプログラマが呼び出されてアクセスできない)Personを受け取ります。だからあなたが呼ぶなら__proto__Person.prototype

var myPerson = new Person();
myPerson.describe();

JSはオブジェクトメンバーをオブジェクト内で直接検索し、次にプロトタイプなどで最大まで検索するため、機能しますObject.prototype

重要なのは、2番目のケースでは、関数のインスタンスが1つだけ存在するということです。あなたはおそらくそれがより良いデザインであることに同意するでしょう。そして、あなたがそうしなくても、それは単により少ないメモリを必要とします。

于 2012-08-10T15:02:48.930 に答える
6

そのコードには何の問題もありません。これはおそらく意味されていることです:

function Person(name) {
    this.name = name;
}
Person.prototype.age = 15; //<= adding a hardcoded property to the prototype

今、あなたはこれを見るでしょう:

var pete = new Person('Pete'), mary = new Person('Mary');
pete.age; //=> 15
mary.age  //=> 15

そして、ほとんどの場合、それはあなたが望むものではありません。コンストラクターのプロトタイプに割り当てられたプロパティはすべてのインスタンス間で共有され、コンストラクター(this.name)内で割り当てられたプロパティはインスタンスに固有です。

于 2012-08-10T15:03:28.537 に答える
3

arxanasが言うように、この記事ではデータのプロパティについて言及しています。

その理由は、データは通常インスタンスに固有であるため、プロトタイプに追加しても意味がないためだと思います。

さらに、データが配列などの可変タイプであり、それをプロトタイプに割り当てる場合、この配列インスタンスはすべてのインスタンス間で共有され、すべてのインスタンスが独自の配列を持っているかのように使用することはできません。


例:以下は、誤った動作につながります。

function Set() {

}

// shared between instances
// each instance adds values to **the same** array
Set.prototype.elements = [];

Set.prototype.add = function(x) {
   this.elements.push(x);
};

そのはず:

function Set() {
    // each instance gets its own array
    this.elements = [];
}

Set.prototype.add = function(x) {
   this.elements.push(x);
};

要約すると:

  • すべてのインスタンス間で共有する必要のあるプロパティをプロトタイプに追加します。
  • コンストラクター関数内でインスタンス固有のデータを割り当てます。
于 2012-08-10T14:58:25.573 に答える
1

arxanasが彼のコメントに書いたように。プロトタイプのデータプロパティは、従来のoopのクラスレベルの変数とほぼ同じです。そして、あなたが非常に特別な必要がない限り、これらは日常的に使用されません。それで全部です。

于 2012-08-10T14:59:55.017 に答える
1

プロトタイプでプロパティを宣言することは、アンチパターンではありません。オブジェクトを見ると、prototype「これが、このタイプの典型的なオブジェクトがデータとメソッドに対して持っているものです」と思います。

他の人は、プロトタイプでプロパティに参照値を与えることに対して警告しています。たとえばFoo.prototype.bar = [];、配列とオブジェクトは参照型であるためです。参照型は不変であるため、「クラス」のすべてのインスタンスは同じ配列またはオブジェクトを参照します。nullそれらをプロトタイプで設定し、コンストラクターで値を指定するだけです。

非常に明確な理由の1つとして、常にすべてのプロパティをプロトタイプに含めます。他のプログラマーに、コンストラクターを調べて把握することなく、公開されているプロパティとそのデフォルト値を伝えることです。

これは、ドキュメントが必要な共有ライブラリを作成する場合に特に役立ちます。

この例を考えてみましょう。

/**
 * class Point
 * 
 * A simple X-Y coordinate class
 *
 * new Point(x, y)
 * - x (Number): X coordinate
 * - y (Number): Y coordinate
 *
 * Creates a new Point object
 **/
function Point(x, y) {
    /**
     * Point#x -> Number
     *
     * The X or horizontal coordinate
     **/
    this.x = x;

    /**
     * Point#y -> Number
     *
     * The Y or vertical coordinate
     **/
    this.y = y;
}

Point.prototype = {
    constructor: Point,

    /**
     * Point#isAbove(other) -> bool
     * - other (Point): The point to compare this to
     *
     * Checks to see if this point is above another
     **/
    isAbove: function(other) {
        return this.y > other.y;
    }
};

(ドキュメント形式:PDoc

xおよびyプロパティに関する情報がコンストラクター関数内に埋め込まれているため、ここではドキュメントを読むだけでは少し厄介です。これらのプロパティをプロトタイプに含める「アンチパターン」とは対照的です。

/**
 * class Point
 * 
 * A simple X-Y coordinate class
 *
 * new Point(x, y)
 * - x (Number): X coordinate
 * - y (Number): Y coordinate
 *
 * Creates a new Point object
 **/
function Point(x, y) {
    this.x = x;
    this.y = y;
}

Point.prototype = {

    /**
     * Point#x -> Number
     *
     * The X or horizontal coordinate
     **/
    x: 0,

    /**
     * Point#y -> Number
     *
     * The Y or vertical coordinate
     **/
    y: 0,

    constructor: Point,

    /**
     * Point#isAbove(other) -> bool
     * - other (Point): The point to compare this to
     *
     * Checks to see if this point is above another
     **/
    isAbove: function(other) {
        return this.y > other.y;
    }

};

ここでプロトタイプを見ると、実際のオブジェクトのスナップショットが得られます。これは、頭の中で視覚化するのがはるかに簡単で、作成者がドキュメントを作成するのも簡単です。Pointコンストラクター関数もドキュメントで雑然としておらず、オブジェクトに命を吹き込むというビジネスに固執しています。

プロトタイプにはすべてが含まれており、メソッドとデータの両方について「プロトタイプ」Pointオブジェクトが持つものに関する標準的な情報源です。

プロトタイプにデータプロパティを含めないことがアンチパターンであると私は主張します。

于 2014-10-28T17:14:09.233 に答える
1

おそらく関連:一般に、所有していないオブジェクトを変更すると、アンチパターンと見なされます。

つまり、オブジェクトを作成しなかった場合、そのオブジェクトを「所有」することはありません。含む:

  • ネイティブオブジェクト(オブジェクト、配列など)
  • DOMオブジェクト
  • ブラウザオブジェクトモデル(BOM)オブジェクト(などwindow
  • ライブラリオブジェクト

ニコラスC.ザカスによる保守可能なJavascriptのソース

于 2020-06-23T12:48:56.517 に答える