开发者

Pattern for CoffeeScript modules [duplicate]

开发者 https://www.devze.com 2023-02-15 07:12 出处:网络
This question already has answers here: Why use the javascript function wrapper (added in coffeescript) ".call(this)"
This question already has answers here: Why use the javascript function wrapper (added in coffeescript) ".call(this)" (2 answers) 开发者_开发百科 Closed 8 years ago.

While reviewing the source code for CoffeeScript on Github, I noticed that most, if not all, of the modules are defined as follows:

(function() {
    ...
}).call(this);

This pattern looks like it wraps the entire module in an anonymous function and calls itself.

What are the pros (and cons) of this approach? Are there other ways to accomplish the same goals?


Harmen's answer is quite good, but let me elaborate a bit on where this is done by the CoffeeScript compiler and why.

When you compile something with coffee -c foo.coffee, you will always get a foo.js that looks like this:

(function() {
  ...
}).call(this);

Why is that? Well, suppose you put an assignment like

x = 'stringy string'

in foo.coffee. When it sees that, the compiler asks: Does x already exist in this scope, or an outer scope? If not, it puts a var x declaration at the top of that scope in the JavaScript output.

Now suppose you write

x = 42

in bar.coffee, compile both, and concatenate foo.js with bar.js for deployment. You'll get

(function() {
  var x;
  x = 'stringy string';
  ...
}).call(this);
(function() {
  var x;
  x = 42;
  ...
}).call(this);

So the x in foo.coffee and the x in bar.coffee are totally isolated from one another. This is an important part of CoffeeScript: Variables never leak from one .coffee file to another unless explicitly exported (by being attached to a shared global, or to exports in Node.js).

You can override this by using the -b ("bare") flag to coffee, but this should only be used in very special cases. If you used it with the above example, the output you'd get would be

var x;
x = 'stringy string';
...
var x;
x = 42;
...

This could have dire consequences. To test this yourself, try adding setTimeout (-> alert x), 1 in foo.coffee. And note that you don't have to concatenate the two JS files yourself—if you use two separate <script> tags to include them on a page, they still effectively run as one file.

By isolating the scopes of different modules, the CoffeeScript compiler saves you from the headache of worrying whether different files in your project might use the same local variable names. This is common practice in the JavaScript world (see, for instance, the jQuery source, or just about any jQuery plugin)—CoffeeScript just takes care of it for you.


The good thing about this approach is that it creates private variables, so there won't be any conflict with variable names:

(function() {
  var privateVar = 'test';
  alert(privateVar); // test
})();

alert(typeof privateVar); // undefined

The addition of .call(this) makes the this keyword refer to the same value as it referred to outside the function. If it is not added, the this keyword will automatically refer to the global object.

A small example to show the difference follows:

function coffee(){
  this.val = 'test';
  this.module = (function(){
    return this.val;
  }).call(this);
}

var instance = new coffee();
alert(instance.module); // test

function coffee(){
  this.val = 'test';
  this.module = (function(){
    return this.val;
  })();
}

var instance = new coffee();
alert(typeof instance.module); // undefined


This is similar syntax to this:

(function() {

}());

which is called an immediate function. the function is defined and executed immediately. the pros to this is that you can place all of your code inside this block, and assign the function to a single global variable, thus reducing global namespace pollution. it provides a nice contained scope within the function.

This is the typical pattern i use when writing a module:

var MY_MODULE = (function() {
    //local variables
    var variable1,
        variable2,
        _self = {},
        etc

    // public API
    _self = {
       someMethod: function () {

       }
    }

    return _self;
}());

not sure what the cons might be exactly, if someone else knows of any i would be happy to learn about them.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号