プロトタイプとクロージャを使用することで最もよく解決されるプログラミングの一般的な問題は何ですか?


8

両方の概念を理解している限り、インスタンス化および/またはカプセル化されたクラスのようなブロックを作成するためにそれらを使用する以外に、JavaScriptのクロージャーとプロトタイプをどのように利用できるのかわかりません(これは私にとってアセットよりも回避策のようです) )

値としての関数や非ブール値の論理評価など、他のJS機能は、恋に落ちるのがはるかに簡単です...

プロポタイプの継承とクロージャを使用することで最もよく解決される一般的なプログラミングの問題は何ですか?


1
オブジェクト指向は、典型的な継承によって解決されます。JavaScriptでは、プロトタイプがOOメカニズムです。そしてクロージャーは、状態を関数にバインドするために使用するものです。node.jsなど、非同期ロジックを頻繁に使用するものを試してみることをお勧めします。そうすれば、簡単にクロージャーに夢中になります。
Raynos、

多分私は自分自身を十分に明確にしていない-私はプロトタイプを介してOOを達成することをすでに認識しているが、その機能のより多くの使用法がなければならないのではないか?非同期ロジックの例を自由に追加してください。
vemv

1
プロトタイプを介してOOを達成することはマイナーな偉業であるように聞こえます。「プロトタイプはOOよりも多くの用途があるはずです」
Raynos

2
@vemv:OOを提供するためのプロトタイプがあります。限目。それ以外のものは本当に虐待です。
Jan Hudec、

2
@vemv彼らはエキゾチックではありません、javascriptコミュニティはプロトタイプを教えることにおいて非常に貧弱な仕事をしているだけですプロトタイプについて読んでください
レイノス

回答:


5
  1. クロージャは、関数を値として有用なものにするものです。関数を渡すとき、ほぼ確実に何らかのコンテキストを引き継ぐ必要があります。これは、クロージャーが行うこととまったく同じです。

  2. プロトタイプは、クラス継承の単純なバージョンにすぎません。インスタンスとクラス(動的言語では特別な種類のインスタンスで表されます)ではなく、インスタンスだけがあり、プロトタイプがクラスです(プロトタイプは基本クラスです)。つまり、基本的には同じ問題を解決します。プロトタイプの方が実装が簡単で(JavaScriptが選択したのはそのためです)、使用するのがやや難しく(まあ、構文上の砂糖がないだけです)、悪用しやすくなります。


「関数を渡すときは、ほぼ確実にコンテキストが必要です」というわけではありません。Cとその関数ポインターを見てください。コンテキストは必要ありません。通常、関数を渡す場合は、これらの関数に必要なコンテキストを与えます。確かにクロージャはコンテキストを渡すのに最適な方法ですが、唯一のものとは程遠い
Raynos

「プロトタイプは実装が簡単です(それがJavaScriptがそれらを選択した理由です)」これは、何の参照もないあいまいな(そして、誤ったステートメントである可能性が高い)です。
Raynos

@Raynos:Cでは、void* data引数を介して関数にコンテキストを渡し、呼び出された関数によってダウンキャストされることがよくあります。
kevin cline

1
@Raynos:私はC関数ポインターを調べていますが、それらを取得するコードがコンテキスト付きの余分なvoid *を通過しない限り、それらを使用するのは非常に困難です。プロトタイプに関しては、1種類の非プリミティブオブジェクトとメンバーにアクセスする1つの統一された方法(インスタンスとクラスのメンバーを分離せず、メタオブジェクトの特別な動作がない)があるため、プロトタイプの実装は明らかに簡単です。ECMAScriptデザイナーがこの理由で実際にプロトタイプを選択したというリファレンスはありませんが、最小限のインタープリターが重要な設計目標であったため、可能性が非常に高いです。
ジャン・ヒューデック

@JanHudec JavaScriptがプロトタイプを持っている可能性は数桁高いと思います。なぜなら、プロトタイプのOO言語Selfがjavascriptを書いたときにブレンダンに影響を与えたからです。
レイノス

4

クロージャーは、それなしでは解決できないプログラミング問題を解決しません。ただし、これはチューリングの完全性に必要のない言語機能でも言えるので、あまり意味がありません。

クロージャーを使用しないように、クロージャーを使用するコードをどのように書き直す必要があるかを考えてください。おそらく、関数に呼び出しの間に状態を保持できるように、クロージャーに追加のプロパティを追加します。これは、既にスコープ内にある変数を関数の同じ名前のプロパティにコピーするだけなので、画面に「なぜ愚かなコンパイラ(通訳、なんでも)これを理解しますか?」それがクロージャです。愚かなコンパイラそれを理解するのに十分スマートです。


昨日、私は最初にクロージャーを意味のある方法で使用する方法に気づきました-それらは懸念の分離をはるかに軽くすることができます:1つのコンポーネントが匿名関数を別のコンポーネントに送信し、その本体は2番目のコンポーネントが認識していないフィールドへの参照を作成します。ベースのコンパイラに感謝します!
vemv

@vemv-2番目のコンポーネントが認識していないフィールドへの参照を保持するオブジェクトを作成して2番目のコンポーネントに渡すことで、クロージャーなしでできることですが、クロージャーと比較すると非常に面倒です。それをしないこと。
psr

2

クロージャーは非同期ロジックに最適です。

それは主に私のためのコードの編成についてです。コードが何をしているかを分割するためにたくさんのローカル関数を持つことは素晴らしいことです。

create: function _create(post, cb) {
    // cache the object reference
    var that = this;

    function handleAll(err, data) {
        var rows = data.rows;

        var id = rows.reduce(function(memo, item) {
            var id = +item.id.split(":")[1];
            return id > memo ? id : memo;
        }, 0);
        id++;


        var obj = {
            title: post.title,
            content: post.content,
            id: id,
            // refer to the object through the closure
            _id: that.prefix + id,
            datetime: Date.now(),
            type: "post"
        }

        PostModel.insert(obj, handleInsert);
    }

    // this function doesn't use the closure at all.
    function handleInsert(err, post) {
        PostModel.get(post.id, handleGet);
    }

    // this function references cb and that from the closure
    function handleGet(err, post) {
        cb(null, that.make(post));
    }

    PostModel.all(handleAll);
}

これがクロージャの別の例です

var cachedRead = (function() {
    // bind cache variable to the readFile function
    var cache = {};

    function readFile(name, cb) {
        // reference cache
        var file = cache[name];
        if (file) {
            return cb(null, file);
        }

        fs.readFile(name, function(err, file) {
            if (file) cache[name] = file;
            cb.apply(this, arguments);
        });
    }

    return readFile;
})();

そして別の例

create: function _create(uri, cb, sync) {
    // close over count
    var count = 3;

    // next only fires cb if called three times
    function next() {
        count--;
        // close over cb
        count === 0 && cb(null);
    }

    // close over cb and next
    function errorHandler(err, func) {
        err ? cb(err) : next();
    }

    // close over cb and next
    function swallowFileDoesNotExist(err, func) {
        if (err && err.message.indexOf("No such file") === -1) {
            return cb(err);
        }
        next();
    }

    this.createJavaScript(uri, swallowFileDoesNotExist, sync)

    this.createDocumentFragment(uri, errorHandler, sync);

    this.createCSS(uri, swallowFileDoesNotExist, sync);
},

クロージャーを使用する代わりに、を使用して変数を関数にカリー化しますf.bind(null, curriedVariable)

ただし、一般に、非同期プログラミングロジックはコールバックを使用し、コールバックの状態の操作はカリー化またはクロージャに依存しています。個人的には閉鎖を好む。

プロトタイプ継承の使用については、OOを許可しますか?プロトタイプの継承は、それが「有用」であると見なされるために、実際にそれ以上のことを行う必要がありますか?それは継承ツールであり、継承を可能にし、それは十分に便利です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.