立即执行函数

原文:http://web.jobbole.com/82520/

"立即执行函数"其实就是"立即执行函数表达式",这句话很重要,下面的文字就是描述为什么这么说。

函数表达式

var fn = funtion(){
    /* code */
}

函数声明

function fn(){
  /* code */  
}

函数声明必须是一个单独的JavaScript语句。

1.介绍函数(函数声明,函数表达式)

在JavaScript中,每一个函数在被调用的时候都会创建一个执行上下文,在该函数内部定义的变量和函数只能在该函数内部使用,而正是因为这个上下文,让我们在调用函数的时候能创建一些私有变量。

(图1)

为什么函数one可以访问外部的i的值,可以参考:原文https://github.com/creeperyang/blog/issues/16

简单描述一下,函数上下文:每调用一次产生一个上下文,调用完毕后销毁。

 

在该上下文中,有个规律,里面的函数可以访问外部的函数的属性,而外部却不能访问函数的内部属性。在es6中就有块的概念了。

为啥有这个规律?

最小访问原则:

不允许你代码中所有的东西在任意地方都可用的好处是什么?其中一个优势,是作用域为你的代码提供了一个安全层级。计算机安全中,有个常规的原则是:用户只能访问他们当前需要的东西。

想想计算机管理员吧。他们在公司各个系统上拥有很多控制权,看起来甚至可以给予他们拥有全部权限的账号。假设你有一家公司,拥有三个管理员,他们都有系统的全部访问权限,并且一切运转正常。但是突然发生了一点意外,你的一个系统遭到恶意病毒攻击。现在你不知道这谁出的问题了吧?你这才意识到你应该只给他们基本用户的账号,并且只在需要时赋予他们完全的访问权。这能帮助你跟踪变化并记录每个人的操作。这叫做最小访问原则。眼熟吗?这个原则也应用于编程语言设计,在大多数编程语言(包括 JavaScript)中称为作用域。(引用http://web.jobbole.com/91134/)

回到问题的核心。

修改图(1)的代码.

// makeCounter函数返回的是一个新的函数,该函数对makeCounter里的局部变量i享有使用权
function makeCounter() {
  // i只是makeCounter函数内的局部变量
  var i = 0;
 
  return function() {
    console.log( ++i );
  };
}
 
// 注意counter和counter2是不同的实例,它们分别拥有自己范围里的i变量
 
var counter = makeCounter();
counter(); // 1
counter(); // 2
 
var counter2 = makeCounter();
counter2(); // 1
counter2(); // 2
 
i; // 报错,i没有定义,它只是makeCounter内部的局部变量

那么,立即执行函数其实就是加一个小括号,那么

//列子1

function (){
 /*code*/        
}()

//报错SyntaxError: Unexpected token (

在JavaScript代码解释时,当遇到function关键词时,会默认把他当成一个函数声明,而不是函数表达式,如果没有把它显视地表达成函数表达式,就报错了,因为函数声明需要一个函数名,而上面的代码中函数没有函数名。(以上代码,也正是在执行到第一个左括号(时报错,因为(前理论上是应该有个函数名的。)

加个函数名

//列子2

function foo(){
/*code*/
}()

//依旧报错 SyntaxError: Unexpected token )

为什么会这样?在一个表达式后面加上括号,表示该表达式立即执行;而如果是在一个语句后面加上括号,该括号完全和之前的语句不搭嘎,而只是一个分组操作符,用来控制运算中的优先级(小括号里的先运算)。

如果没有优先级的话,这句话是对的,但是小括号的优先级比function的高,所以就会报错

//修改之后的代码

(function foo(){
 /*code*/
}())

为什么这样就能立即执行并且不报错呢?因为在javascript里,括号内部不能包含语句,当解析器对代码进行解释的时候,先碰到了(),然后碰到function关键字就会自动将()里面的代码识别为函数表达式而不是函数声明

还有一些其他的骚操作:

// 这是一个自执行函数,函数内部执行的是自己,递归调用
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 */ }());
 
// 立即执行函数也可以自执行,不过不常用罢了
(function(){ arguments.callee(); }());
(function foo(){ foo(); }());
原文地址:https://www.cnblogs.com/damai/p/9008655.html