Czy taki sposób definiowania obiektów JS ma jakiś cel?


87

Utrzymuję jakiś starszy kod i zauważyłem, że używany jest następujący wzorzec do definiowania obiektów:

var MyObject = {};

(function (root) {

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

})(MyObject);

Czy ma to jakiś cel? Czy jest to równoważne wykonaniu następujących czynności?

var MyObject = {

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

};

Nie mam zamiaru podejmować świętych poszukiwań refaktoryzacji całego kodu według własnych upodobań, ale naprawdę chciałbym zrozumieć powód tego okrężnego sposobu definiowania obiektów.

Dzięki!


1
W twoim dokładnym przykładzie nie ma różnicy. Jeśli ją rozszerzysz, może być różnica, ale pojawią się również różne podejścia, które również pojawią się w grze.
Travis J

Nie ma znaczenia, obiekty są przekazywane jako kopia odniesienia, że ​​tak powiem, więc nawet podczas definiowania funkcji myFunction wewnątrz IIFE jest ona nadal dostępna poza nim.
adeneo

1
@adeneo Nie w tym przykładzie, by myFunctionmożna było użyć pewnych zmiennych zdefiniowanych poza sobą, które nie byłyby dostępne z zewnątrz. Zobacz moją odpowiedź
Juan Mendes

2
możliwy duplikat Jak nazywa się ten wzorzec JavaScript i dlaczego jest używany? (nie wiem, czy powinienem zamknąć). Zobacz także Deklaracja przestrzeni nazw JavaScript lub ta .
Bergi,

Odpowiedzi:


116

Nazywa się to wzorcem modułu http://toddmotto.com/mastering-the-module-pattern/

Głównym powodem jest tworzenie prawdziwie prywatnych metod i zmiennych. W twoim przypadku nie ma to znaczenia, ponieważ nie ukrywa żadnych szczegółów implementacji.

Oto przykład, w którym sensowne jest użycie wzorca modułu.

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

Natomiast gdybyś użył tylko prostego przedmiotu adderi valuestałbyś się publiczny

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

I możesz to złamać, robiąc takie rzeczy

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

Ale z wersją modułu

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
To tak naprawdę nie odpowiada na pytanie, jaka jest różnica między tymi dwoma alternatywami?

20
@torazaburo Przykład OP nie był dobrym przykładem, podałem prawdziwy przykład, który pokazuje, kiedy użyć wzorca modułu.
Juan Mendes,

To ns.getNext: function () {się nie skompiluje.
punund

Zrobiłbym to, gdybym był pewien, jak to naprawić. Myślałem, że jest jakiś konstrukt, któremu należy zapobiec delete MyNameSpace.getNext.
punund

2
@punund JS nie ma kompilatora, ma interpreter:)
frogatto

22

Celem jest ograniczenie dostępności funkcji w domknięciu, aby uniemożliwić innym skryptom wykonywanie na nim kodu. Owijając go wokół zamknięcia , przedefiniowujesz zakres wykonania dla całego kodu wewnątrz zamknięcia i skutecznie tworzysz zakres prywatny. Zobacz ten artykuł, aby uzyskać więcej informacji:

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

Z artykułu:

Jednym z najbardziej znanych problemów JavaScript jest jego zależność od zasięgu globalnego, co w zasadzie oznacza, że ​​wszystkie deklarowane zmienne poza funkcją znajdują się w tej samej przestrzeni nazw: złowieszczym obiekcie okna. Ze względu na naturę stron internetowych wiele skryptów z różnych źródeł może (i będzie) działać na tej samej stronie o wspólnym zasięgu globalnym i może to być naprawdę bardzo zła rzecz, ponieważ może prowadzić do kolizji nazw (zmienne o tych samych nazwach zostanie nadpisany) i kwestie bezpieczeństwa. Aby zminimalizować problem, możemy użyć potężnych zamknięć JavaScript do stworzenia prywatnych zakresów, w których możemy mieć pewność, że nasze zmienne są niewidoczne dla innych skryptów na stronie.



Kod:

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
myFunctionnie ma zakresu globalnego w drugiej wersji. Celem jest w rzeczywistości zapewnienie zakresu, w którym można by zdefiniować wewnętrzne funkcje pomocnicze.
Barmar

myFunctionnależy do zakresu globalnego, ponieważ jest zdefiniowany w obiekcie globalnym myObject. W drugiej wersji można było wykonać dowolny inny kod w aplikacji myFunction. W pierwszej wersji tylko kod w zamknięciu ma dostęp domyFunction
Jonathan Crowe

Nie, myFunctionmoże być wykonane tylko jako MyObject.myFunction(), tak samo jak pierwsza wersja.
Barmar

@JonathanCrowe Przykład OP nie jest dobrym przykładem, ujawnia wszystko wewnątrz modułu, więc tak, staje się dostępny na zewnątrz. Zobacz moją odpowiedź na przydatny przypadek modułu
Juan Mendes,

@JuanMendes to dobra uwaga, przykład OP nie jest świetnym zastosowaniem wzorca modułu
Jonathan Crowe,

6

Zalety:

  1. utrzymuje zmienne w zakresie prywatnym.

  2. możesz rozszerzyć funkcjonalność istniejącego obiektu.

  3. wydajność jest zwiększona.

Myślę, że powyższe trzy proste punkty wystarczą, aby przestrzegać tych zasad. A żeby było to proste, to nic innego jak pisanie funkcji wewnętrznych.


6

W konkretnym przypadku, który pokazujesz, nie ma znaczącej różnicy pod względem funkcjonalności lub widoczności.

Jest prawdopodobne, że oryginalny programista przyjął to podejście jako rodzaj szablonu pozwalającego mu zdefiniować prywatne zmienne, które mogą być użyte w definicji rzeczy takich jak myFunction:

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

Pozwala to uniknąć obliczeń za seconds_per_daykażdym razem, gdy wywoływana jest funkcja, jednocześnie chroniąc ją przed zanieczyszczeniem zakresu globalnego.

Jednak nic zasadniczo nie różni się od tego i po prostu mówimy

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

Oryginalny koder mógł preferować możliwość dodawania funkcji do obiektu przy użyciu deklaratywnej składni programu root.myFunction = function, zamiast składni obiektu / właściwości myFunction: function. Ale ta różnica jest głównie kwestią preferencji.

Jednak struktura przyjęta przez oryginalnego kodera ma tę zaletę, że właściwości / metody można łatwo dodać w innym miejscu kodu:

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);

Podsumowując, nie ma potrzeby przyjmowania tego podejścia, jeśli nie jest to konieczne, ale nie ma też potrzeby, aby go zmieniać, ponieważ działa ono doskonale i faktycznie ma pewne zalety.


6
  1. Pierwszy wzorzec może być użyty jako moduł, który pobiera obiekt i zwraca ten obiekt z pewnymi modyfikacjami. Innymi słowy, możesz zdefiniować takie moduły w następujący sposób.

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

    I używaj go jak:

    var obj = {};
    module(obj);
    

    Zatem zaletą może być możliwość ponownego wykorzystania tego modułu do późniejszych zastosowań.


  1. W pierwszym wzorcu możesz zdefiniować zakres prywatny do przechowywania twoich prywatnych rzeczy, takich jak prywatne właściwości i metody. Na przykład rozważ ten fragment:

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

  1. Ten wzorzec może służyć do dodawania metody lub właściwości do wszystkich typów obiektów, takich jak tablice, literały obiektów, funkcje.

    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
    

Moim zdaniem główną zaletą może być ta druga (stworzenie zakresu prywatnego)


5

Ten wzorzec zapewnia zakres, w którym można zdefiniować funkcje pomocnicze, które nie są widoczne w zakresie globalnym:

(function (root) {

    function doFoo() { ... };

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

})(MyObject);

doFoo jest lokalna dla funkcji anonimowej, nie można się do niej odwoływać z zewnątrz.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.