深入理解函数声明和函数表达式、深入理解立即执行函数(自执行函数)

在学习函数声明和函数表达式之前如果你对作用域和作用域链掌握的不是特别的好,建议您先看完js深入理解函数作用域和作用域链,再进行接下来的学习

函数声明:function 函数名(){}

函数表达式:function  函数名(){},函数名,可写可不写,有名函数是命名函数表达式,无函数名是匿名函数表达式

长得那么像,怎么判断是函数声明还是函数表达式呢?其实就是看上下文判断的

var a = function aaa(){}、(function aaa(){})、以及在前面加位于运算符的都是函数表达式,(~、+、-、!……)

函数声明和函数表达式的区别是什么?

1、函数表达式可以直接在后面加括号执行,而函数声明不可以

function aaa(){alert(1);}();//报错
var a = function aaa(){alert(1);}();//正确
~function aaa(){alert(1);}();//正确

2、函数声明在js解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值实在js运行时确定的,并且在表达式赋值完之后,该函数才能调用。

var bar = function foo(){}; // 表达式,因为它是赋值表达(AssignmentExpression)的一部分
 
new function bar(){}; // 表达式,因为它是New表达式(NewExpression)的一部分
(function foo(){}); // 函数表达式:注意它被包含在一对圆括号中的函数,在其上下文环境中,()构成了一个分组操作符,而分组操作符只能包含表达式
callMe(function () {  });
//传递一个函数作为参数 // 这是一个匿名函数表达式
callMe(function me() { }); // 传递一个函数作为参数,函数名为me// 这是一个具名函数表达式

 FunctionDeclaration(函数声明)只能出现在Program(程序)或FunctionBody(函数体)内。从句法上讲,它们 不能出现在Block(块)({ ... })中,例如不能出现在 if、while 或 for 语句中。因为 Block(块) 中只能包含Statement(语句), 而不能包含FunctionDeclaration(函数声明)这样的SourceElement(源元素)。

  另一方面,仔细看一看产生规则也会发现,唯一可能让Expression(表达式)出现在Block(块)中情形,就是让它作为ExpressionStatement(表达式语句)的一部分。但是,规范明确规定了ExpressionStatement(表达式语句)不能以关键字function开头。而这实际上就是说,FunctionExpression(函数表达式)同样也不能出现在Statement(语句)或Block(块)中(别忘了Block(块)就是由Statement(语句)构成的)。

  由于存在上述限制,只要函数出现在块中(像上面例子中那样),实际上就应该将其看作一个语法错误,而不是什么函数声明或表达式。

聊完函数声明和函数表达式,现在我们来看看自执行函数是个什么鬼

自执行函数又被称为立即调用函数,就是函数声明后可以立即被执行,在js库中我们可以经常发现自执行函数的身影,现在我们就用实例来说一下什么是自执行函数。

经常需要一个函数自执行,可惜这一种写法是错的: 代码如下:

function(){alert(1);}(); 
原因是前半段“function(){alert(1);}”被当成了函数声明,而不是一个函数表达式,从而让后面的“();”变得孤立,产生语法错。 
按上面的分析,这一段代码虽说没有语法错,但也是不符合我们的预期的,因为这个函数并没有自执行。  代码如下:
function(){alert(1);}(1); 
综上,症结在于,如何明确代码描述的是一个函数表达式,而不是函数声明语句。 
正确的写法多种多样,也各有利弊: 
方法1:最前最后加括号  代码如下:
(function(){alert(1);}()); 
这是jslint推荐的写法,好处是,能提醒阅读代码的人,这段代码是一个整体。 
例如,在有语法高亮匹配功能的编辑器里,光标在第一个左括号后时,最后一个右括号也会高亮,看代码的人一眼就可以看到这个整体。 
不过,对于某些写代码不喜欢在行后加分号的同学,也会形成一些坑坑,例如以下代码会报运行错:  代码如下:
var a=1 
(function(){alert(1);}()); 
方法2:function外面加括号  代码如下:
(function(){alert(1);})(); 
这种做法比方法1少了一个代码整体性的好处。 
方法3:function前面加运算符,常见的是!与void 。  代码如下:
!function(){alert(1);}(); 
void function(){alert(2);}(); 
下面附上汤姆大叔的博客对自执行函数的理解
// 这是一个自执行的函数,函数内部执行自身,递归
function foo() { foo(); }

// 这是一个自执行的匿名函数,因为没有标示名称
// 必须使用arguments.callee属性来执行自己
var foo = function () { arguments.callee(); };

// 这可能也是一个自执行的匿名函数,仅仅是foo标示名称引用它自身
// 如果你将foo改变成其它的,你将得到一个used-to-self-execute匿名函数
var foo = function () { foo(); };

// 有些人叫这个是自执行的匿名函数(即便它不是),因为它没有调用自身,它只是立即执行而已。
(function () { /* code */ } ());

// 为函数表达式添加一个标示名称,可以方便Debug
// 但一定命名了,这个函数就不再是匿名的了
(function foo() { /* code */ } ());

// 立即调用的函数表达式(IIFE)也可以自执行,不过可能不常用罢了
(function () { arguments.callee(); } ());
(function foo() { foo(); } ());

// 另外,下面的代码
在黑莓5里执行会出错,因为在一个命名的函数表达式里,他的名称是undefined
// 呵呵,奇怪
(function foo() { foo(); } ());
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值