(function() {})()
以及它在jQuery中的对应形式(function($) {})(jQuery)
经常出现在JavaScript代码中。
这些结构是如何工作的,以及它们解决了哪些问题?
请提供示例。
(function() {})()
以及它在jQuery中的对应形式(function($) {})(jQuery)
经常出现在JavaScript代码中。
这些结构是如何工作的,以及它们解决了哪些问题?
请提供示例。
$
符号被用于许多不同的场合。为了缓解可能的冲突,您可以使用这些构造:(function ($){
// Your code using $ here.
})(jQuery);
具体来说,这是一个匿名函数声明,它会立即执行并将主要的jQuery对象作为参数传递。在该函数内部,您可以使用$
来引用该对象,而无需担心其他框架也在作用域中。
这是一种限制变量作用域的技巧;这是防止变量污染全局命名空间的唯一方法。
var bar = 1; // bar is now part of the global namespace
alert(bar);
(function () {
var foo = 1; // foo has function scope
alert(foo);
// code to be executed goes here
})();
1)它定义了一个匿名函数,并立即执行该函数。
2)通常这样做是为了避免在全局命名空间中添加不需要的代码。
3)您需要从其中公开一些方法,任何在函数内声明的内容都将是“私有”的,例如:
MyLib = (function(){
// other private stuff here
return {
init: function(){
}
};
})();
或者,另一种选择是:
MyLib = {};
(function({
MyLib.foo = function(){
}
}));
问题在于,你可以用很多方法来使用它,但结果始终如一。
这只是一个被立即调用的匿名函数。你也可以先创建这个函数,然后再调用它,得到同样的效果:
(function(){ ... })();
作用如下:
temp = function(){ ... };
temp();
你也可以使用命名函数实现同样的效果:
function temp() { ... }
temp();
你所称为jQuery特定代码只是因为它在其中使用了jQuery对象。它只是一个带参数的匿名函数,会立即调用。
你可以用两个步骤完成同样的事情,并且可以使用任何你喜欢的参数:
temp = function(answer){ ... };
temp(42);
这解决的问题是在函数中创建闭包。你可以在其中声明变量而不污染全局命名空间,从而降低使用一个脚本与另一个脚本时发生冲突的风险。对于 jQuery 的特定情况,您可以在兼容模式下使用它,该模式不会将名称 $ 声明为 jQuery 的别名。通过将 jQuery 对象发送到闭包中并将参数命名为 $,您仍然可以使用与无兼容模式相同的语法。它在这里解释了,你的第一个构造函数提供了变量作用域。
在javascript中,变量的作用域是函数级别的。这与像C#或Java这样的语言不同,其中变量的作用域限于块。这意味着如果你在循环或if语句内声明一个变量,它将对整个函数可用。
如果你发现自己需要在函数内明确地给变量分配作用域,可以使用匿名函数来实现。你实际上可以创建一个匿名函数,然后立即执行它,所有内部的变量都将被限定在匿名函数中:
(function() {
var myProperty = "hello world";
alert(myProperty);
})();
alert(typeof(myProperty)); // undefined
另一个做这件事的原因是消除你使用哪个框架的$
操作符的混淆。例如,要强制使用jQuery,可以这样做:
;(function($){
... your jQuery code here...
})(jQuery);
通过将$
运算符作为参数传递并在jQuery上调用它,即使您加载了其他框架,该函数内部的$
运算符也会被锁定到jQuery。
这种结构的另一个用途是“捕获”将在闭包中使用的局部变量的值。例如:
for (var i = 0; i < 3; i++) {
$("#button"+i).click(function() {
alert(i);
});
}
for (var i = 0; i < 3; i++) {
(function(i) {
$("#button"+i).click(function() {
alert(i);
});
})(i);
}
这被认为是一个闭包。这意味着包含的代码将在其自己的词法作用域内运行。这意味着您可以定义新变量和函数,并且它们不会与闭包外部使用的命名空间发生冲突。
var i = 0;
alert("The magic number is " + i);
(function() {
var i = 99;
alert("The magic number inside the closure is " + i);
})();
alert("The magic number is still " + i);
这将生成三个弹出窗口,演示闭包中的i
不会改变同名的预先存在的变量:
就像其他人所说的一样,它们都定义了立即调用的匿名函数。我通常会在JavaScript类声明中使用这个结构,以创建一个静态私有作用域,使该作用域中的常量数据、静态方法、事件处理程序或其他任何内容仅对类的实例可见。
// Declare a namespace object.
window.MyLibrary = {};
// Wrap class declaration to create a private static scope.
(function() {
var incrementingID = 0;
function somePrivateStaticMethod() {
// ...
}
// Declare the MyObject class under the MyLibrary namespace.
MyLibrary.MyObject = function() {
this.id = incrementingID++;
};
// ...MyObject's prototype declaration goes here, etc...
MyLibrary.MyObject.prototype = {
memberMethod: function() {
// Do some stuff
// Maybe call a static private method!
somePrivateStaticMethod();
}
};
})();
MyObject
类被分配到MyLibrary
命名空间中,因此可以访问。 incrementingID
和somePrivateStaticMethod()
不能在匿名函数作用域之外直接访问。基本上就是给你的 JavaScript 代码打上了命名空间。
例如,你可以把任何变量或函数放在其中,而从外部来看,它们不存在于该作用域中。因此,当你将所有内容封装在其中时,就不必担心冲突问题。
结尾处的()
表示自我调用。你还可以在那里添加一个参数,它将成为匿名函数的参数。我经常在 jQuery 中使用这种方式,你可以看到原因……
(function($) {
// Now I can use $, but it won't affect any other library like Prototype
})(jQuery);
Evan Trimboli在他的回答中涵盖了其余部分。
jQuery
作为参数提供。 - Georg Schölly