为什么Backbone.js代码前面是(function($){...?

ale*_*nco 1 backbone.js

这是Backbone.js代码的一个示例(取自教程):

// **This example illustrates the binding of DOM events to View methods.**
//
// _Working example: [2.html](../2.html)._  
// _[Go to Example 3](3.html)_

//
(function($){
  var ListView = Backbone.View.extend({
    el: $('body'), // el attaches to existing element
    // `events`: Where DOM events are bound to View methods. Backbone doesn't have a separate controller to handle such bindings; it all happens in a View.
    events: {
      'click button#add': 'addItem'
    },
    initialize: function(){
      _.bindAll(this, 'render', 'addItem'); // every function that uses 'this' as the current object should be in here

      this.counter = 0; // total number of items added thus far
      this.render();
    },
    // `render()` now introduces a button to add a new list item.
    render: function(){
      $(this.el).append("<button id='add'>Add list item</button>");
      $(this.el).append("<ul></ul>");
    },
    // `addItem()`: Custom function called via `click` event above.
    addItem: function(){
      this.counter++;
      $('ul', this.el).append("<li>hello world"+this.counter+"</li>");
    }
  });

  var listView = new ListView();      
})(jQuery);
Run Code Online (Sandbox Code Playgroud)

我不明白为什么必须先加一个 (function($){....

任何人都可以向我解释这个吗?

Tom*_*han 7

这是编写jQuery插件时的常见做法.

原因是您希望避免与可能也在$全局范围中使用该符号的其他库发生冲突.通过将代码包装在一个带有一个参数(命名$)的函数调用中并将该jQuery对象传递给该函数,可以确保避免冲突.

function($) { // declare an anonymous function that takes an argument $

    // conflict safe code =)

}(jQuery); // pass the argument jQuery to the anonymous function
Run Code Online (Sandbox Code Playgroud)

正如nielsbot所指出的,如果给函数命名,可能更容易看到上面代码的作用:

var executeSafely = function($) {

    // conflict safe code =)

}

executeSafely(jQuery);
Run Code Online (Sandbox Code Playgroud)

我知道这是这样的事实后,却MikeG刚指出,在这个信息的评论中,这未必是一件坏事做与其他地方的JavaScript代码.我以为我会扩展一些原因:

范围界定的案例

假设您的网站上有两个独立的JavaScript模块.比方说,你已经写了他们的代码在这两个js文件module-one.jsmodule-two.js.通常,页面上只有一个模块,因此在创建其中一个模块时,您不关心另一个模块发生了什么(您甚至可能有不同的开发人员独立于另一个模块工作).

然后,您决定在开始页面上显示这些内容会很好,因此您在起始页面html的head部分中包含以下内容:

<script type="text/javascript" src="module-one.js" />
<script type="text/javascript" src="module-two.js" />
Run Code Online (Sandbox Code Playgroud)

[提示:世界末日音乐!]一切都崩溃了!到底发生了什么?

您查看模块的代码,并查看以下内容:

模块one.js:

var state = 0;

function init() {
    ...
}

etc...
Run Code Online (Sandbox Code Playgroud)

模块two.js:

var state = 2;

function init() {
    ...
}

etc ...
Run Code Online (Sandbox Code Playgroud)

这里发生了什么?

好吧,页面按顺序加载两个脚本 - 第一个模块 - one.js被加载,一切都是第一个模块; 然后,加载module-two.js,并覆盖第一个模块中定义的一些变量!这是因为在JavaScript中定义全局范围中的变量,它被隐式解释为在window对象上定义它.因此,如果您在彼此之后运行两个脚本文件,例如,您首先设置window.state = 0,然后window.state = 2.如您所见,模块并非真正独立.例如,在加载两个脚本后调用init()(或实际上window.init())时,只启动第二个模块 - 第一个init()函数被覆盖(因为window只能有一个属性命名init)并且不再存在.

寻求救援

为了避免这个问题,你应该在函数调用中包装你的模块,就像你上面的jQuery插件一样.但是,这些函数不需要采用任何参数:

模块one.js

(function() {
    // code for module one here
})();
Run Code Online (Sandbox Code Playgroud)

模块two.js

(function() {
    // code for module two here
})();
Run Code Online (Sandbox Code Playgroud)

现在,由于这两个模块是在单独的函数内定义的,因此所有变量的范围仅限于在这些函数中生效.如果您不确定如何使用JavaScript代码,将其包装在这样的函数调用中是避免冲突的好方法.因为如果你不经常跟随它们就很难记住好的做法,所以一直跟着这个做法并不会伤害任何人=)