7

私たちは、Stoyan Stefanov の本を研究し、'new'、'this'、'prototype'、クロージャなどに関する無限の SO 投稿を読んで、JS アプリでオブジェクトを処理する最善の方法について議論してきました。非常に多くの競合する理論があるため、完全に明白な答えがないことを示唆しています)。

それでは、個人データは気にしないとしましょう。私たちは、ユーザーと開発者が、私たちが定義した方法以外のオブジェクトをいじらないことを信頼して満足しています。

これを考えると、(何十年にもわたるオブジェクト指向のスタイルと歴史に逆らっているように見えること以外に) この手法の何が問題になるのでしょうか?

// namespace to isolate all PERSON's logic
var PERSON = {};

// return an object which should only ever contain data.
// The Catch: it's 100% public

PERSON.constructor = function (name) {
     return {
         name: name
     } 
}

// methods that operate on a Person
// the thing we're operating on gets passed in

PERSON.sayHello = function (person) {
     alert (person.name); 
}

var p = PERSON.constructor ("Fred");
var q = PERSON.constructor ("Me");

// normally this coded like 'p.sayHello()'
PERSON.sayHello(p);
PERSON.sayHello(q);

明らかに:

  1. 誰かが不浄な方法で 'p' を変異させたり、単に PERSON の論理があちこちに広がったりするのを止めるものは何もないでしょう。(これは、正規の「新しい」手法にも当てはまります)。
  2. 使用したいすべての関数に 'p' を渡すのは少し面倒です。
  3. これは奇妙なアプローチです。

しかし、それらはそれを却下するのに十分な理由ですか? 肯定的な側面:

  1. (ほぼ間違いなく) 反復的な関数宣言を伴うクロージャーとは対照的に、効率的です。
  2. どこでも「これ」をいじるのとは対照的に、非常にシンプルで理解しやすいようです。

重要な点は、前述のプライバシーです。私はこれで非難されることを知っていますが、フィードバックを探しています。乾杯。

4

2 に答える 2

9

本質的に悪いことは何もありません。しかし、Javascript のプロトタイプ システムの使用に固有の多くの利点が失われます。

  • オブジェクトは、それがオブジェクト リテラルであること以外、それ自体について何も知りません。そのため、その起源を特定するのに役立ちinstanceofません。ダックタイピングだけでは行き詰まります。
  • あなたのメソッドは本質的に名前空間化された static functionsであり、最初の引数としてオブジェクトを渡して繰り返す必要があります。プロトタイプ化されたオブジェクトを持つことにより、動的ディスパッチを利用できるため、Javascript が認識している型に対して、またはそれに応じp.sayHello()てさまざまなことを行うことができます。これはポリモーフィズムの一種です。あなたのアプローチでは、メソッドを呼び出すたびに型に名前を付ける必要があります (おそらく間違いを犯す可能性があります)。PERSONANIMAL
  • constructor関数はすでにオブジェクトであるため、実際には関数は必要ありません。PERSON変数はコンストラクター関数でもかまいません。

ここで行ったことは、モジュールパターン (名前空間など) を作成することです。

持っているものを保持しながら、上記の利点を提供する別のパターンを次に示します。

function Person(name)
{
    var p = Object.create(Person.prototype);
    p.name = name; // or other means of initialization, use of overloaded arguments, etc.
    return p;
}
Person.prototype.sayHello = function () { alert (this.name); }

var p = Person("Fred"); // you can omit "new"
var q = Person("Me");
p.sayHello();
q.sayHello();
console.log(p instanceof Person); // true
var people = ["Bob", "Will", "Mary", "Alandra"].map(Person);
        // people contains array of Person objects
于 2013-02-06T01:16:35.203 に答える
1

ええ、コンストラクターのアプローチを回避しようとしている理由や、コンストラクター自体がエレガントで柔軟な、そして、Crockford のような人々が OOP を好まない理由をどれだけ与えても、OOP への完全に合理的なアプローチです (新しいキーワードを使用するのを忘れているためです - 真剣に?)。JS は関数駆動型であり、その OOP メカニズムも例外ではありません。IMO、それから隠れるよりも、これを受け入れる方が良い.

まず、「当然」の下に記載されているポイント

  1. JavaScript で言及する価値さえほとんどありません。高度な可変性は設計によるものです。私たちは自分自身や JavaScript の他の開発者を恐れていません。プライベート対パブリックのパラダイムは、愚かさから私たちを守るためではなく、他の開発者のコ​​ードの背後にある意図を理解しやすくするため、役に立ちません。

  2. 呼び出す努力は問題ではありません。やったことの理由が不明な場合、問題は後で発生します。コア言語のアプローチがあなたにとってより良い結果をもたらさないということで、あなたが何を達成しようとしているのか、私にはよくわかりません。

  3. これは JavaScript です。何年もの間、JS 開発者以外のすべての人にとっては奇妙でした。特定のドメインの問題を解決するのに、より一般的な解決策よりもうまく機能する何かを行うためのより良い方法を見つけたとしても、それを気にしないでください。他の言語パラダイムから JS に移行するときに多くの人が持っているように、より一般的なアプローチを置き換える前に、より典型的なアプローチのポイントを理解していることを確認してください。JS で些細なことをするのは簡単ですが、もっと OOP を使いたいと思うようになったら、コア言語がどのように機能するかについてできる限りのことを学んでください。 JavaScriptを実際よりも恐ろしく、致命的なブービートラップでいっぱいにする副業をしている人々によって。

「プラス面」の下にあるあなたのポイントは、

  1. まず第一に、反復的な関数定義は、実際には重いループのシナリオで心配するだけのものでした. プロトタイプ化されていない public メソッド定義がパフォーマンスの問題になるのに十分な速さで十分な量のオブジェクトを定期的に生成していた場合、おそらく、自明ではないオブジェクトのメモリ使用量の問題にすぐに遭遇するでしょう。ただし、過去形で話しているのは、どちらにしても、もはや実際には関連する問題ではないからです。最新のブラウザーでは、他の関数内で定義された関数は、最新の JIT コンパイラーの動作方法により、実際には通常、パフォーマンスが向上します。サポートしているブラウザーに関係なく、オブジェクトごとに定義されたいくつかの関数は、何万ものオブジェクトが予想される場合を除き、問題ではありません。

  2. シンプルで理解しやすいかどうかについては、私にはわかりません。なぜなら、あなたがここで獲得した勝利が見えないからです。使用するオブジェクトを 1 つ持つ代わりに、オブジェクトとその疑似コンストラクターの両方を一緒に使用する必要があります。これは、定義を見ていない場合、オブジェクトを構築するために「new」キーワードで使用する関数を意味します。 . 私があなたのコードベースに不慣れだったら、私が理解していなかった他の懸念を壊すのを避けるために、なぜあなたがこのようにしたのかを理解しようとして多くの時間を無駄にしていただろう.

私の質問は次のとおりです。

そもそもコンストラクターのオブジェクト リテラルにすべてのメソッドを追加しないのはなぜですか? そこにはパフォーマンスの問題はなく、実際にあったこともないので、新しいオブジェクトを作成した後に person に新しいメソッドを追加できるようにすることだけが唯一の可能な勝利ですが、それが適切なコンストラクターでプロトタイプを使用するものです。 (プロトタイプ メソッドは一度しか定義されないため、古いブラウザのメモリには最適です)。

また、メソッドがプロパティを知るためにオブジェクトを渡し続ける必要がある場合、なぜオブジェクトが必要なのでしょうか? 特定のプロパティを持つ単純なデータ構造型オブジェクトを期待する関数だけではないのはなぜですか? もうOOPではありません。

しかし、私の主な批判点は

OOP の主なポイントが欠けています。これは、JavaScript がほとんどの言語よりも人々から隠れていないという点で優れています。次の点を考慮してください。

function Person(name){
    //var name = name; //<--this might be more clear but it would be redundant
    this.identifySelf = function(){ alert(name); }
}

var bob = new Person();
bob.identifySelf();

ここで、オブジェクトまたはメソッドを上書きせずに、bob が識別する名前を変更します。これは、最初に設計および構築されたオブジェクトを操作したくないことが明らかな場合にのみ行うことです。もちろんできません。これにより、この定義を見た人には、この場合名前が事実上定数であることは明らかです。より複雑なコンストラクターでは、名前を変更または変更できるのはインスタンス自体だけであることが確立されます。ただし、ユーザーが非検証セッター メソッドを追加した場合は、基本的に (Java Enterprise Beans を見て) MURDER THE OOP の中心的な目的。

明確な責任分担がカギ

彼らがすべての本に入れているキーワードを一瞬忘れて、全体のポイントが何であるかを考えてください. OOP が登場する前は、すべてが関数とデータ構造の山にすぎませんでした。OOP では、ほとんどの場合、実際にはオブジェクト自体のみが変更される一連のデータにバンドルされた一連のメソッドがあります。

それでは、出力で何か問題が発生したとしましょう。

  • 厳密に手続き型の関数の山では、そのデータを台無しにする可能性のあるハンドの数に実際の制限はありません。エラー処理は適切かもしれませんが、元の原因を突き止めるのが難しいような方法で 1 つの関数が分岐する可能性があります。

  • 通常、データがオブジェクト ゲートキーパーの背後にある適切な OOP 設計では、1 つのオブジェクトだけが実際に責任を持って変更を加えることができることがわかっています。

ほとんどの場合、すべてのデータを公開するオブジェクトは、古い手続き型アプローチよりもわずかに優れているだけです。緩く関連するメソッドを分類するための名前を付けるだけです。

「これ」について大騒ぎ

「this」キーワードが乱雑で紛らわしいことに過度の注意が向けられていることを、私は理解したことがありません。それほど大したことではありません。「this」は、作業しているインスタンスを識別します。それでおしまい。メソッドがプロパティとして呼び出されない場合、どのインスタンスを探すべきかわからないため、デフォルトでグローバル オブジェクトになります。それはばかげていました (undefined の方が良かったでしょう) が、関数がデータのように移植可能であり、他のオブジェクトに非常に簡単にアタッチできる言語では、そのシナリオで適切に機能しないことが予想されます。次の場合は、関数で「this」を使用します。

  • インスタンスのプロパティとして定義され、呼び出されます。

  • これはイベント ハンドラーとして渡されます (リッスンされているもののメンバーとして呼び出されます)。

  • call または apply メソッドを使用して、他のオブジェクトのプロパティとして一時的に割り当てずに呼び出しています。

しかし、覚えておいてください、本当に重要なのは召しです。public メソッドをいくつかの var に割り当て、その var から呼び出すと、グローバルな処理が行われるか、strict モードでエラーがスローされます。オブジェクトのプロパティとして参照されていない場合、関数は定義されたスコープ (クロージャ) と渡された引数のみを気にします。

于 2013-02-11T00:31:33.177 に答える