Why is this function wrapped in parentheses, followed by parentheses?
我一直在javascript源代码中看到这一点,但我从未真正发现使用这个构造的真正原因。为什么需要这样做?
1 2 3 4 5 | (function() { //stuff })(); |
为什么这样写?为什么不单独使用
编辑:我知道这是在定义一个匿名函数,然后调用它,但是为什么呢?
这定义了一个函数闭包
这用于创建一个函数闭包,其中包含私有功能和不全局可见的变量。
请考虑以下代码:
1 2 3 | (function(){ var test = true; })(); |
变量
函数闭包使得不同的脚本不相互干扰成为可能,即使它们定义了相似的命名变量或私有函数。这些私人空间是可见的,只能在封闭区内进入,而不能在封闭区外进入。
检查此代码并阅读注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // public part var publicVar = 111; var publicFunc = function(value) { alert(value); }; var publicObject = { // no functions whatsoever }; // closure part (function(pubObj){ // private variables and functions var closureVar = 222; var closureFunc = function(value){ // call public func publicFunc(value); // alert private variable alert(closureVar); }; // add function to public object that accesses private functionality pubObj.alertValues = closureFunc; // mind the missing"var" which makes it a public variable anotherPublic = 333; })(publicObject); // alert 111 & alert 222 publicObject.alertValues(publicVar); // try to access varaibles alert(publicVar); // alert 111 alert(anotherPublic); // alert 333 alert(typeof(closureVar)); // alert"undefined" |
下面是一个运行JSfiddle的代码,它按照上面代码中的注释显示数据。
它实际上做了什么?你已经知道了
创建函数:
1 | function() { ... } |
立即执行:
1 | (func)(); |
此函数可能接受或不接受其他参数。
jquery插件通常是这样定义的,通过用一个插件在其中操作的参数定义一个函数:
1 | (function(paramName){ ... })(jQuery); |
但是主要的想法还是一样的:用私有定义定义一个函数闭包,它不能直接在函数闭包外部使用。
这个构造被称为一个自执行匿名函数,它实际上不是一个很好的名字,下面是发生的事情(以及为什么这个名字不是一个好名字)。这是:
1 2 3 | function abc() { //stuff } |
定义一个名为
1 2 3 | function() { //stuff } |
但是,如果您有这个变量,您要么需要将它与一个变量相关联,这样您就可以调用它(这将使它不那么匿名),要么需要立即执行它。我们可以通过这样做来尝试直接执行它:
1 2 3 | function() { //stuff }(); |
但这不会起作用,因为它会给你一个语法错误。出现语法错误的原因如下。当您创建一个具有名称的函数(如上面的abc)时,该名称将成为对函数表达式的引用,然后您可以通过将()放在名称后面来执行表达式,例如:
1 2 3 | (function() { //stuff })(); |
一个立即执行的匿名函数(您可以看到构造的名称有点偏离,因为它实际上不是一个执行自身的匿名函数,而是一个直接执行的匿名函数)。
好吧,那么为什么所有这些都有用,一个原因是它允许您阻止代码污染全局名称空间。因为JavaScript中的函数有自己的作用域,所以函数中的任何变量在全局上都是不可见的,所以如果我们能够在函数中编写所有代码,那么全局作用域是安全的,那么我们的自执行匿名函数就允许我们这样做。让我借用约翰·雷西格的旧书中的一个例子:
1 2 3 4 5 6 7 8 9 10 11 | // Create a new anonymous function, to use as a wrapper (function(){ // The variable that would, normally, be global var msg ="Thanks for visiting!"; // Binding a new function to a global object window.onunload = function(){ // Which uses the 'hidden' variable alert( msg ); }; // Close off the anonymous function and execute it })(); |
我们所有的变量和函数都写在自执行匿名函数中,我们的代码首先执行,因为它在自执行匿名函数中。由于javascript允许闭包,即本质上允许函数访问在外部函数中定义的变量,所以我们可以在自执行匿名函数中编写我们喜欢的任何代码,并且一切仍将按预期工作。
但是,等等,还有更多的。这个构造允许我们解决在JavaScript中使用闭包时有时会出现的问题。我再次让约翰·雷西格解释,我引用:
Remember that closures allow you to reference variables that exist
within the parent function. However, it does not provide the value of
the variable at the time it is created; it provides the last value of
the variable within the parent function. The most common issue under
which you’ll see this occur is during a for loop. There is one
variable being used as the iterator (e.g., i). Inside of the for loop,
new functions are being created that utilize the closure to reference
the iterator again. The problem is that by the time the new closured
functions are called, they will reference the last value of the
iterator (i.e., the last position in an array), not the value that you
would expect. Listing 2-16 shows an example of using anonymous
functions to induce scope, to create an instance where expected
closure is possible.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // An element with an ID of main var obj = document.getElementById("main"); // An array of items to bind to var items = ["click","keypress" ]; // Iterate through each of the items for ( var i = 0; i < items.length; i++ ) { // Use a self-executed anonymous function to induce scope (function(){ // Remember the value within this scope var item = items[i]; // Bind a function to the element obj["on" + item ] = function() { // item refers to a parent variable that has been successfully // scoped within the context of this for loop alert("Thanks for your" + item ); }; })(); } |
本质上,所有这些都意味着这一点,人们经常这样编写幼稚的javascript代码(这是上面的循环的幼稚版本):
1 2 3 4 5 6 7 | for ( var i = 0; i < items.length; i++ ) { var item = items[i]; // Bind a function to the elment obj["on" + item ] = function() { alert("Thanks for your" + items[i] ); }; } |
我们在循环中创建的函数是闭包,但不幸的是,它们将从封闭范围锁定
1 2 3 4 5 6 7 | for ( var i = 0; i < items.length; i++ ) { (function(index){ obj["on" + item ] = function() { alert("Thanks for your" + items[index] ); }; })(i); } |
因为我们在每次迭代中都调用匿名函数,所以我们传入的参数被锁定为它传入时的值,所以我们在循环中创建的所有函数都将按预期工作。
现在,有两个很好的理由来使用自执行匿名函数构造,以及为什么它首先实际工作。
它用于定义一个匿名函数,然后调用它。我没有尝试过,但我对块周围为什么有paren的最好猜测是因为javascript需要它们来理解函数调用。
如果您想在适当的地方定义一个一次性函数,然后立即调用它,那么它是很有用的。使用匿名函数和只写代码的区别在于范围。当函数结束时,匿名函数中的所有变量都将超出作用域(当然,除非变量被告知其他情况)。这可以用来保持全局或封闭的名称空间的清洁,长期使用较少的内存,或者获得一些"隐私"。
我不确定这个问题是否已经被回答了,所以如果我只是重复一些东西,请道歉。
在JavaScript中,只有函数引入了新的作用域。通过将代码包装在即时函数中,您定义的所有变量只存在于这个或更低的范围内,而不存在于全局范围内。
所以这是一个不污染全球范围的好方法。
应该只有几个全局变量。记住,每个全局都是
当您想要创建一个闭包时,这样的构造是有用的——一个构造有助于为从外部无法访问的变量创建一个私有的"房间"。请参阅"javascript:The Good Parts"一章中的更多内容:http://books.google.com/books?id=pxa2bby0oq0c&pg=pa37&plp=pa37&dq=crockford+clockford+clocloclockcal+立即调用&source=bl&ots=hilk8x4jl&sig=-t-t0jtmf7 twzacq5_5aj3a&hl=lv&;ei=Lsa5tax x x x x x x x&sa=x&;oi=book-result&ct=res结果&resnum=1&;ved=0buq6aewaa \瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦瓦v=一页&q&f=假
在第38页顶部所示的示例中,您可以看到变量"status"隐藏在一个闭包中,除了调用
它是"匿名自执行函数"或"立即调用函数表达式"。本·阿尔曼的解释很好。
创建名称空间时使用该模式
1 2 3 4 5 6 7 | var APP = {}; (function(context){ })(APP); |