Есть ли у этого способа определения объектов JS какая-либо цель?

87

Я поддерживаю унаследованный код и заметил, что для определения объектов используется следующий шаблон:

var MyObject = {};

(function (root) {

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

})(MyObject);

Есть ли в этом какая-то цель? Это эквивалентно простому выполнению следующего?

var MyObject = {

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

};

Я не собираюсь пускаться в священный квест по рефакторингу всей кодовой базы по своему вкусу, но мне действительно хотелось бы понять причину такого окольного способа определения объектов.

Благодарность!

Себастьян Ванстенкисте
источник
1
В вашем точном примере разницы нет. Если вы расширите его, может быть разница, но тогда также будут разные подходы, которые будут использоваться.
Travis J
Это не имеет значения, объекты передаются как копия ссылки, так сказать, поэтому даже при определении myFunction внутри IIFE он все еще доступен за его пределами.
adeneo 06
1
@adeneo Не в этом примере, by myFunctionможет использовать некоторые переменные, определенные вне себя, которые не будут доступны извне. Смотрите мой ответ
Хуан Мендес
2
возможный дубликат Как называется этот шаблон JavaScript и почему он используется? (не уверен, стоит ли закрывать). См. Также Декларацию пространства имен JavaScript или эту .
Берги

Ответы:

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
Это не совсем ответ на вопрос, в чем разница между двумя альтернативами?
20
@torazaburo Пример OP не был хорошим примером, я привел реальный пример, который показывает, когда использовать шаблон модуля.
Хуан Мендес
Это ns.getNext: function () {не скомпилируется.
punund
Был бы, если бы знал, как это исправить. Я думал, что есть какая-то конструкция, которую нужно предотвратить delete MyNameSpace.getNext.
punund
2
@punund JS не имеет компилятора, у него есть интерпретатор:)
frogatto
22

Цель состоит в том, чтобы ограничить доступность функций в закрытии, чтобы предотвратить выполнение кода другими скриптами . Оборачивая его вокруг замыкания, вы переопределяете объем выполнения для всего кода внутри замыкания и эффективно создаете частную область. См. Эту статью для получения дополнительной информации:

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

Из статьи:

Одна из наиболее известных проблем JavaScript - его зависимость от глобальной области видимости, что в основном означает, что любые переменные, которые вы объявляете вне функции, находятся в одном и том же пространстве имен: зловещем объекте окна. Из-за природы веб-страниц многие сценарии из разных источников могут (и будут) запускаться на одной и той же странице с общей глобальной областью видимости, и это может быть действительно очень плохо, поскольку может привести к конфликту имен (переменные с одинаковыми именами перезаписываются) и вопросы безопасности. Чтобы свести к минимуму проблему, мы можем использовать мощные закрытия 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
myFunctionне входит в глобальную область видимости во второй версии. На самом деле цель состоит в том, чтобы предоставить область, в которой могут быть определены внутренние вспомогательные функции.
Barmar
myFunctionнаходится в глобальной области видимости, поскольку определен в глобальном объекте myObject. Во второй версии мог выполняться любой другой код в приложении myFunction. В первой версии только код в закрытии имеет доступ кmyFunction
Джонатан Кроу
Нет, myFunctionможет выполняться только как MyObject.myFunction(), как и в первой версии.
Barmar
@JonathanCrowe Пример OP не является хорошим примером, он демонстрирует все внутри модуля, так что да, он становится доступным снаружи. См. Мой ответ о полезном модульном футляре
Хуан Мендес
@JuanMendes - хороший аргумент, пример OP не очень хорошо использует шаблон модуля
Джонатан Кроу,
6

преимущества:

  1. поддерживает переменные в частной области.

  2. вы можете расширить функционал существующего объекта.

  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
    

На мой взгляд, главным преимуществом может быть второе (создание частной области видимости).

лягушка
источник
5

Этот шаблон предоставляет область видимости, в которой вы можете определять вспомогательные функции, которые не видны в глобальной области:

(function (root) {

    function doFoo() { ... };

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

})(MyObject);

doFoo является локальным для анонимной функции, на него нельзя ссылаться извне.

Barmar
источник