深刻理解 函数、匿名函数、自执行匿名函数

1.基础概念:定义函数的方式

通常定义函数有两种方式:javascript

  1. 函数的声明
  2. 函数表达式

1.1函数的声明

以下方法 add 就是函数声明的代码结构:html

function add(x,y){  
   alert(x+y) 
}  
add(1,2) //弹窗显示:3  
复制代码

关于函数声明,它最重要的一个特征就是函数声明提高,意思是执行代码以前先读取函数声明。这意味着能够把函数声明放在调用它的语句以后。以下代码能够正确执行:java

add(1,2); //弹窗显示:3  
function add(x,y){  
    alert(x+y) 
}  
复制代码

1.2函数表达式

函数表达式中有几种不一样的语法。最多见和最具表明性的一种以下所示:bash

var add = function(x,y){  
    alert(x+y) 
}
add(1,2) //弹窗显示:3
复制代码

这种形式看起来好像是常规的变量赋值语句。可是函数表达式和函数声明的区别在于,函数表达式在使用前必须先赋值。因此这段代码执行的时候就会出错:函数

add(1,2) //无弹窗,报错: add is not a function  
var add = function(x,y){  
    alert(x+y)  
} 
复制代码

形成这种现象是由于解析器在向执行环境中加载数据时,解析器会率先读取函数声明,并使其在执行任何代码前可用;至于函数表达式,则必须等到解析器执行到它的所在的的代码行,才会真正的被解析。函数表达式中,建立的函数叫作匿名函数,由于function关键字后面没有标识符。测试

2.匿名函数的调用方式

匿名函数,顾名思义就是没有名字的函数。上面的函数表达式中的建立,其实是建立一个匿名函数,并将匿名函数赋值给变量 add,用 add 来进行函数的调用,调用的方式就是在变量 add 后面加上一对括号(),若是有参数传入的话就是 add(1,2),这就是匿名函数的一种调用方式。ui

还有一种匿名函数的调用方式是:使用()将匿名函数括起来,而后后面再加一对小括号(包含参数列表)。咱们再看一下如下一个例子:spa

alert((function(x,y){return x+y;})(2,3)) //弹窗提示5  
alert((new Function("x","y","return x+y"))(2,3)) //弹窗显示5  
复制代码

在javascript中,是没有块级做用域这种说法的,以上代码的这种方式就是模仿了块级做用域(一般成为私有做用域),语法以下所示:.net

(function(){  
    //这里是块级做用域  
})();  
复制代码

以上代码定义并当即调用了一个匿名函数。经函数声明包含在一对圆括号中,表示它其实是一个函数表达式。而紧随其后的另外一对圆括号会当即调用这个函数。然而要注意一点:code

function(){  
      
}();  
复制代码

上面的代码是错误的,由于Javascript将function关键字看成一个函数声明的开始,而函数声明后面不能加圆括号,若是你不显示告诉编译器,它会默认生成一个缺乏名字的function,而且抛出一个语法错误,由于function声明须要一个名字。有趣的是,即使你为上面那个错误的代码加上一个名字,他也会提示语法错误,只不过和上面的缘由不同。提示为:Uncaught SyntaxError: Unexpected token (

在一个表达式后面加上括号(),该表达式会当即执行,可是在一个语句后面加上括号(),是彻底不同的意思,只是分组操做符。

function foo(){
   alert('测试是否弹窗')
}() 
// SyntaxError: Unexpected token ) 
// 报错由于分组操做符须要包含表达式

function foo(){ 
    alert('测试是否弹窗')
}(1) 
// (1) => 等价于 1
// 至关于foo方法后面个跟了一个无关系的表达式子:(1)
复制代码

因此上面代码要是想要获得想要的弹窗提示,就必需要实现赋值,如

a = function(){
    alert('测试是否弹窗')
}()
// 弹窗提示成功
复制代码

"a=" 这个片断告诉了编译器这个是一个函数表达式,而不是函数的声明。由于函数表达式后面能够跟圆括号。

所以下面两段代码是等价的

var aa = function(x){  
    alert(x)  
}(5) //弹窗显示:5  
复制代码
(function(x){
    alert(x)
})(5) //弹窗显示:5    
复制代码

从上面对于函数和匿名函数的了解,咱们引伸出来了一个概念,即自执行函数。那为何 a =function(){}() 这种表示方法可让编译器认为这个是一个函数表达式而不是一个函数的声明?

3.自执行匿名函数

自执行函数,即定义和调用合为一体。咱们建立了一个匿名的函数,并当即执行它,因为外部没法引用它内部的变量,所以在执行完后很快就会被释放,关键是这种机制不会污染全局对象。

下面咱们来看下一些比较有趣的自执行函数表达方式:

// 下面2个括弧()都会当即执行  
(function () { /* code */ } ()) // 推荐使用这个  
(function () { /* code */ })() // 可是这个也是能够用的  
  
// 因为括弧()和JS的&&,异或,逗号等操做符是在函数表达式和函数声明上消除歧义的  
// 因此一旦解析器知道其中一个已是表达式了,其它的也都默认为表达式了  
var i = function () { return 10; } ();  
true && function () { /* code */ } ();  
0, function () { /* code */ } ();  
  
// 若是你不在乎返回值,或者不怕难以阅读
// 你甚至能够在function前面加一元操做符号  
!function () { /* code */ } ();  
~function () { /* code */ } ();  
-function () { /* code */ } ();  
+function () { /* code */ } ();  
  
// 还有一个状况,使用new关键字,也能够用,但我不肯定它的效率  
// http://twitter.com/kuvos/status/18209252090847232  
new function () { /* code */ }  
new function () { /* code */ } () // 若是须要传递参数,只须要加上括弧()  
复制代码

4.总结

看到这里相信你们对函数有了全新的认识,但愿可以帮助到你们。

文中如有写的不对或须要改进的或者有不一样的简介欢迎回复一块儿探讨。😛

📖 参考连接

相关文章
相关标签/搜索