JSオブジェクトを定義するこの方法には目的がありますか?


87

私はいくつかのレガシーコードを維持していますが、オブジェクトを定義するために次のパターンが使用されていることに気付きました。

var MyObject = {};

(function (root) {

    root.myFunction = function (foo) {
        //do something
    };

})(MyObject);

これには目的がありますか?それは単に次のことをすることと同等ですか?

var MyObject = {

    myFunction : function (foo) {
        //do something
    };

};

私は自分の好みに合わせてコードベース全体をリファクタリングする神聖な探求に乗り出すつもりはありませんが、オブジェクトを定義するその遠回りの方法の背後にある理由を本当に理解したいと思います。

ありがとう!


1
あなたの正確な例では違いはありません。それを拡大すると、違いがあるかもしれませんが、それから、プレイするためにやって来る異なるアプローチもあるでしょう。
Travis J

違いはありません。オブジェクトは、いわば参照のコピーとして渡されるため、IIFE内でmyFunctionを定義しても、その外部からアクセスできます。
adeneo 2014年

1
@adeneoこの例でmyFunctionは、外部からアクセスできない、それ自体の外部で定義されたいくつかの変数を使用できます。私の回答を参照してください
フアンメンデス

2
このJavaScriptパターン何と呼ばれ、なぜ使用されるのでしょうか?(私が閉じるべきかどうかわからない)。JavaScript名前空間宣言またはこれも参照してください。
Bergi、2014年

回答:


116

それはモジュールパターンと呼ばれていますhttp://toddmotto.com/mastering-the-module-pattern/

主な理由は、真にプライベートなメソッドと変数を作成することです。あなたの場合、実装の詳細を隠していないので意味がありません。

モジュールパターンを使用することが理にかなっている例を以下に示します。

var MyNameSpace = {};

(function(ns){
    // The value variable is hidden from the outside world
    var value = 0;

    // So is this function
    function adder(num) {
       return num + 1;
    }

    ns.getNext = function () {
       return value = adder(value);
    }
})(MyNameSpace);

var id = MyNameSpace.getNext(); // 1
var otherId = MyNameSpace.getNext(); // 2
var otherId = MyNameSpace.getNext(); // 3

あなただけのストレートオブジェクトを使用し、場合一方adder及びvalue公共なります

var MyNameSpace = {
    value: 0,
    adder: function(num) {
       return num + 1;
    },
    getNext: function() {
       return this.value = this.adder(this.value);
    }
}

そして、あなたは次のようなことをすることでそれを壊すことができます

MyNameSpace.getNext(); // 1
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 1 again
delete MyNameSpace.adder;
MyNameSpace.getNext(); // error undefined is not a function

しかし、モジュールのバージョンでは

MyNameSpace.getNext(); // 1
 // Is not affecting the internal value, it's creating a new property
MyNameSpace.value = 0;
MyNameSpace.getNext(); // 2, yessss
// Is not deleting anything
delete MyNameSpace.adder;
MyNameSpace.getNext(); // no problemo, outputs 3

2
これは、2つの選択肢の違いは何かという質問には実際には答えませんか?

20
@torazaburo OPの例は良い例ではありませんでした。モジュールパターンをいつ使用するかを示す実際の例を用意しました。
ファンメンデス

これns.getNext: function () {はコンパイルされません。
punund 2014年

私はそれを修正する方法がわかっていれば、持っていたでしょう。防ぐための構造がいくつかあると思いましたdelete MyNameSpace.getNext
punund 2014年

2
@punund JSにはコンパイラーがなく、インタープリターがあります:)
frogatto 14年

22

目的は、クロージャー内の関数のアクセシビリティーを制限して、他のスクリプトがその上でコードを実行しないようにすることです。それを周りにラップすることにより、閉鎖をあなたが再定義されている範囲内のすべてのコードの実行のを閉鎖し、効果的にプライベートスコープを作成します。詳細については、この記事を参照してください。

http://lupomontero.com/using-javascript-closures-to-create-private-scopes/

記事から:

JavaScriptで最もよく知られている問題の1つは、グローバルスコープへの依存です。つまり、基本的に、関数の外部で宣言した変数はすべて同じ名前空間に存在します。それは不吉なウィンドウオブジェクトです。Webページの性質上、さまざまなソースからの多くのスクリプトが共通のグローバルスコープを共有する同じページで実行できます(これは、名前の衝突(同じ名前の変数)につながる可能性があるため、本当に悪いことになる可能性があります)上書きされる)およびセキュリティの問題。問題を最小限に抑えるために、JavaScriptの強力なクロージャーを使用して、変数がページ上の他のスクリプトから見えないことを確認できるプライベートスコープを作成できます。



コード:

var MyObject = {};

(function (root) {
    function myPrivateFunction() {
       return "I can only be called from within the closure";
    }

    root.myFunction = function (foo) {
        //do something
    };    

    myPrivateFunction(); // returns "I can only be called from within the closure"

})(MyObject);


myPrivateFunction(); // throws error - undefined is not a function

1
myFunction2番目のバージョンではグローバルスコープにありません。目的は、内部の補助関数を定義できるスコープを提供することです。
Barmar 2014年

myFunctionグローバルオブジェクト内で定義されているため、グローバルスコープ内にありますmyObject。2番目のバージョンでは、アプリケーション内の他のコードが実行される可能性がありmyFunctionます。最初のバージョンでは、クロージャ内のコードのみがアクセスできますmyFunction
Jonathan Crowe '11

いいえ、最初のバージョンと同様に、myFunctionとしてのみ実行できMyObject.myFunction()ます。
Barmar 2014年

@JonathanCrowe OPの例は良い例ではありません。モジュール内のすべてを公開しているので、はい、外部からアクセスできるようになっています。便利なモジュールケースについては、私の回答を参照してください
Juan Mendes

@JuanMendes良い点、OPの例はモジュールパターンの優れた使用法ではありません
Jonathan Crowe

6

利点:

  1. プライベートスコープで変数を維持します。

  2. 既存のオブジェクトの機能を拡張できます。

  3. パフォーマンスが向上します。

上記の3つの簡単な点は、これらのルールに従うのに十分だと思います。そして、それをシンプルに保つためには、内部関数を書くだけです。


6

表示されている特定のケースでは、機能や可視性の点で意味のある違いはありません。

オリジナルのコーダーがこのアプローチを一種のテンプレートとして採用して、次のようなものの定義に使用できるプライベート変数を定義できるようにする可能性がありますmyFunction

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;   // <-- private variable
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

これによりseconds_per_day、関数が呼び出されるたびに計算が行われなくなり、グローバルスコープを汚染することもなくなります。

しかし、それと本質的に何も違いはなく、ただ言っているだけです

var MyObject = function() {
    var seconds_per_day = 24 * 60 * 60;
    return {
        myFunction: function(foo) {
            return seconds_per_day;
        }
    };
}();

元のコーダーはroot.myFunction = function、のオブジェクト/プロパティ構文ではなく、の宣言構文を使用してオブジェクトに関数を追加できることを好んでいた可能性がありますmyFunction: function。しかし、その違いは主に好みの問題です。

ただし、元のコーダーが採用した構造には、プロパティ/メソッドをコードの他の場所に簡単に追加できるという利点があります。

var MyObject = {};
(function(root) {
    var seconds_per_day = 24 * 60 * 60;
    root.myFunction = function(foo) {
        return seconds_per_day;
    };
})(MyObject);

(function(root) {
    var another_private_variable = Math.pi;
    root.myFunction2 = function(bar) { };
})(MyObject);

結論として、必要がない場合はこのアプローチを採用する必要はありませんが、完全にうまく機能し、実際にいくつかの利点があるため、変更する必要もありません。


6
  1. 最初のパターンは、オブジェクトを受け取り、変更を加えてそのオブジェクトを返すモジュールとして使用できます。つまり、次のようにモジュールを定義できます。

    var module = function (root) {
        root.myFunction = function (foo) {
            //do something
        };
    }

    そしてそれを次のように使用します:

    var obj = {};
    module(obj);

    したがって、後で使用するためにこのモジュールを再利用できるという利点があります。


  1. 最初のパターンでは、プライベートスコープを定義して、プライベートプロパティやメソッドなどのプライベートなものを格納できます。たとえば、次のスニペットを考えてみます。

    (function (root) {
    
        // A private property
        var factor = 3;
    
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(MyObject);

  1. このパターンを使用すると、配列、オブジェクトリテラル、関数など、すべてのタイプのオブジェクトにメソッドまたはプロパティを追加できます。

    function sum(a, b) {
        return a + b;
    }
    
    (function (root) {
        // A private property
        var factor = 3;
        root.multiply = function (foo) {
            return foo * factor;
        };
    })(sum);
    
    console.log(sum(1, 2)); // 3
    console.log(sum.multiply(4)); // 12

私の意見では、主な利点は2番目の利点になる可能性があります(プライベートスコープの作成)


5

このパターンは、グローバルスコープでは表示されないヘルパー関数を定義できるスコープを提供します。

(function (root) {

    function doFoo() { ... };

    root.myFunction = function (foo) {
        //do something
        doFoo();
        //do something else
    };

})(MyObject);

doFoo 無名関数に対してローカルであり、外部から参照することはできません。

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