JavaScript 匿名函数
示例
定义一个匿名函数
定义函数后,通常会给它一个名称,然后使用该名称调用它,如下所示:
foo(); function foo(){ //... }
当您以这种方式定义函数时,Javascript运行时会将函数存储在内存中,然后使用您为其分配的名称创建对该函数的引用。然后可以在当前范围内访问该名称。这是创建函数的一种非常方便的方法,但是Javascript不需要您为函数分配名称。以下内容也完全合法:
function() { //... }
当定义一个没有名称的函数时,它称为匿名函数。该函数存储在内存中,但是运行时不会自动为您创建对该函数的引用。乍一看,似乎没有任何用处,但在许多情况下匿名功能非常方便。
将匿名函数分配给变量
匿名函数的一种非常常见的用法是将它们分配给变量:
var foo = function(){ /*...*/ }; foo();
在函数作为变量中将更详细地介绍匿名函数的使用。
将匿名函数作为参数提供给另一个函数
某些函数可以接受对函数的引用作为参数。这些有时称为“依赖注入”或“回调”,因为它允许您的调用函数“回调”您的代码,从而使您有机会更改被调用函数的行为方式。例如,使用Array对象的map函数,您可以遍历数组的每个元素,然后通过对每个元素应用转换函数来构建新的数组。
var nums = [0,1,2]; var doubledNums = nums.map( function(element){ return element * 2; } ); //[0,2,4]
创建一个命名函数将是乏味,草率且不必要的,这将使您的作用域仅在此位置需要一个函数而变得混乱,并破坏了代码的自然流动和可读性(同事必须离开此代码才能找到您的代码)功能以了解发生了什么)。
从另一个函数返回一个匿名函数
有时,将一个函数作为另一个函数的结果返回是很有用的。例如:
var hash = getHashFunction( 'sha1' ); var hashValue = hash( 'Secret Value' ); function getHashFunction( algorithm ){ if ( algorithm === 'sha1' ) return function( value ){ /*...*/ }; else if ( algorithm === 'md5' ) return function( value ){ /*...*/ }; }
立即调用匿名函数
与许多其他语言不同,Javascript的作用域是函数级的,而不是块级的。(请参阅功能范围)。但是,在某些情况下,有必要创建一个新的作用域。例如,通过<script>标签添加代码时通常会创建一个新的作用域,而不是允许在全局作用域中定义变量名(这会冒其他脚本与您的变量名冲突的风险)。处理这种情况的常用方法是定义一个新的匿名函数,然后立即调用它,将变量安全地隐藏在匿名函数的范围内,而无需通过泄漏的函数名使第三方可以访问您的代码。例如:
<!-- My Script --> <script> function initialize(){ //foo安全地隐藏在initialize中,但是... var foo = ''; } //...my initialize function is now accessible from global scope. //有人可能会再次调用它(可能是无意中)。 initialize(); </script> <script> //使用匿名函数,然后立即 //调用它,隐藏我的foo变量并保证 //没有人可以第二次称呼它。 (function(){ var foo = ''; }()) // <--- the parentheses invokes the function immediately </script>
自引用匿名函数
有时,匿名函数能够引用自身很有用。例如,该函数可能需要递归调用自身或为其添加属性。但是,如果函数是匿名的,则可能会非常困难,因为它需要知道已将函数分配给该变量的知识。这是不太理想的解决方案:
var foo = function(callAgain){ console.log( 'Whassup?' ); //不太理想...我们依赖可变参考... if (callAgain === true) foo(false); }; foo(true); //控制台输出: //没话说 //没话说 //将bar分配给原始函数,并将foo分配给另一个函数。 var bar = foo; foo = function(){ console.log('Bad.') }; bar(true); //控制台输出: //没话说 //坏。
此处的目的是让匿名函数递归调用自身,但是当foo的值更改时,最终会导致潜在的难以跟踪的错误。
相反,我们可以通过给匿名函数一个私有名称来为其提供对自身的引用,如下所示:
var foo = function myself(callAgain){ console.log( 'Whassup?' ); //不太理想...我们依赖可变参考... if (callAgain === true) myself(false); }; foo(true); //控制台输出: //没话说 //没话说 //将bar分配给原始函数,并将foo分配给另一个函数。 var bar = foo; foo = function(){ console.log('Bad.') }; bar(true); //控制台输出: //没话说 //没话说
请注意,函数名称仅限于自身。该名称尚未泄漏到外部作用域中:
myself(false); //ReferenceError:我自己未定义
当将递归匿名函数作为回调参数处理时,此技术特别有用:
//计算数组中每个数字的斐波那契值: var fib = false, result = [1,2,3,4,5,6,7,8].map( function fib(n){ return ( n <= 2 ) ? 1 : fib( n - 1 ) + fib( n - 2 ); }); //结果=[1、2、3、5、8、13、21] //fib=false(匿名函数名称未覆盖我们的fib变量)