这种定义 JS 对象的方法有什么用处吗?

我正在维护一些遗留代码,我注意到使用了以下定义对象的模式:

var MyObject = {};


(function (root) {


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


})(MyObject);

这样做有什么目的吗? 它是否等同于只做以下事情?

var MyObject = {


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


};

我并不打算按照自己的喜好重构整个代码库,但我真的很想理解这种迂回定义对象的方式背后的原因。

谢谢!

3269 次浏览

The purpose is to limit accessibility of functions within the closure to help prevent other scripts from executing code on it. By wrapping it around a closure you are redefining the scope of execution for all code inside the closure and effectively creating a private scope. See this article for more info:

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

From the article:

One of the best known problems in JavaScript is its dependance on a global scope, which basically means that any variables you declare outside of a function live in the same name space: the ominous window object. Because of the nature of web pages, many scripts from different sources can (and will) run on the same page sharing a common global scope and this can be a really really bad thing as it can lead to name collisions (variables with the same names being overwritten) and security issues. To minimise the problem we can use JavaScript’s powerful closures to create private scopes where we can be sure our variables are invisible to other scripts on the page.



Code:

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

It's called the module pattern http://toddmotto.com/mastering-the-module-pattern/

The main reason is for you to create truly private methods and variables. In your case, it's not meaningful because it's not hiding any implementation details.

Here's an example where it makes sense to use 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

Whereas if you just used a straight object, adder and value would become public

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

And you could break it by doing stuff like

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

But with the module version

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

This pattern provides a scope in which you can define helper functions that are not visible in the global scope:

(function (root) {


function doFoo() { ... };


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


})(MyObject);

doFoo is local to the anonymous function, it can't be referenced from outside.

In the particular case that you show, there is no meaningful difference, in terms of functionality or visibility.

It's likely that the original coder adopted this approach as a sort of template allowing him to define private variables that could be used in the definition of things like myFunction:

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

This avoids calculating seconds_per_day each time the function is called, while also keeping it from polluting the global scope.

However, there's nothing essentially different from that and just saying

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

The original coder may have preferred to be able to add functions to the object using the declarative syntax of root.myFunction = function, rather than the object/property syntax of myFunction: function. But that difference is mainly a matter of preference.

However, the structure taken by the original coder has the advantage that properties/methods can be easily added elsewhere in the code:

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

Bottom line, there is no need to adopt this approach if you don't need to, but there is also no need to change it, since it works perfectly well and actually has some advantages.

  1. First pattern can be used as a module which takes an object and returns that object with some modifications. In other words, you can define such modules as follows.

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

    And use it like:

    var obj = {};
    module(obj);
    

    So an advantage could be the re-usability of this module for later uses.


  1. In the first pattern, you can define a private scope to store your private stuff such as private properties and methods. For example, consider this snippet:

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

  1. This pattern can be used to add a method or property to all types of objects such as arrays, object literals, functions.

    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
    

In my opinion the main advantage could be the second one (creating a private scope)

advantages:

  1. maintains variables in private scope.

  2. you can extend the functionality of the existing object.

  3. performance is increased.

i think the above three simple points are just enough to follow those rules. And to keep it simple its nothing but writing inner functions.