web-dev-qa-db-ja.com

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

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

var MyObject = {};

(function (root) {

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

})(MyObject);

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

var MyObject = {

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

};

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

ありがとう!

これはモジュールパターンと呼ばれます 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
116
Juan Mendes

その目的は、他のscriptsがその上でコードを実行するのを防ぐためにclosure内の関数のaccessibilityを制限することです。 closureで囲むことにより、scopeの実行をclosure内のすべてのコードに対して再定義し、プライベートスコープを効率的に作成します。詳細については、この記事を参照してください。

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
22
Jonathan Crowe

利点:

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

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

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

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

6
Mateen

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

オリジナルのコーダーがこのアプローチを一種のテンプレートとして採用して、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
user663031
  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番目の利点である可能性があります(プライベートスコープの作成)

6
frogatto

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

(function (root) {

    function doFoo() { ... };

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

})(MyObject);

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

5
Barmar