函数的闭包

一、函数声明、函数表达式、匿名函数

1、函数声明

语法格式:function fnName () {…};
定义:使用function关键字声明一个函数,再指定一个函数名。

2、函数表达式

语法格式:var fnName = function () {…};
定义:使用function关键字声明一个函数,但未给函数命名,最后将匿名函数赋予一个变量。

3、匿名函数

语法格式:function () {…}; 
定义:使用function关键字声明一个函数,但未给函数命名,匿名函数属于函数表达式。
作用:赋予一个变量则创建函数;赋予一个事件则成为事件处理程序;赋予一个事件则创建闭包。

二、加括号后

//函数声明后面加括号,不会报错,但是javascript引擎只解析函数声明,会忽略后面的括号,函数声明不会被调用。
function fnName(){
    alert('Hello World');
}();
//函数表达式后面加括号,当javascript引擎解析到此处时,能立即调用该函数。
var fnName=function(){
    alert('Hello World');
}();
//匿名函数后面加括号,显示语法报错:要求需要一个函数名。虽然匿名函数属于函数表达式,但是未进行赋值操作,所以javascript引擎将开头的function关键字当做函数声明。
function(){
    console.log('Hello World');    
}();

三、函数的闭包、命名空间

函数的闭包(closure)可以让匿名函数立即被执行,这种闭包的优势是:

1、减少了全局变量的个数,可以有效减少命名冲突。原因是包在里面的变量对于外面来说是不可见的,他们的作用域局限在匿名函数的函数体内。

2、这种方式可以保存闭包外面的变量的状态。

多人合作一个项目时,你在全局或局部作用域中声明了一些变量,这些变量可能被其他人的同名变量覆盖掉,根据javascript函数作用域链的特性,可以使用这种技术模仿一个私有作用域,用匿名函数作为一个“容器”,“容器内部可以访问外部的变量,而外部环境不能访问“容器”内部的变量,所以( function(){…} )()内部定义的变量不会和外部的变量发生冲突,俗称“匿名包裹器”或“命名空间”。

Jquery使用的就是这种方法,将Jquery代码包裹在( function (window,undefined){…jquery代码…} (window)中,在全局作用域中调用Jquery代码时,可以保护Jquery内部变量。

四、javascript立即执行函数有哪些方法?

要在函数体后面加括号就能立即调用,则这个函数必须是函数表达式,不能是函数声明。

1、(function(){…})()

相当于先定义 function xx(){},后调用 xx()。是利用匿名函数和闭包来执行…里面的代码,同时所有的定义比如变量的作用域都在闭包里,不会污染到外部命名空间。

function(){…}是一个匿名函数,包裹着function(){…}的()表示拥有最高级的优先执行权。后面的()表示执行完function(){…}后,立即调用function(){…}。

(function(a){
    console.log(a);   //firebug输出123,使用()运算符
})(123);

2、(function (){…} ())

(function(a){
    console.log(a);   //firebug输出1234,使用()运算符
}(1234));

3、其他方法

!function(a){
    console.log(a);   //firebug输出12345,使用!运算符
}(12345);

+function(a){
    console.log(a);   //firebug输出123456,使用+运算符
}(123456);

-function(a){
    console.log(a);   //firebug输出1234567,使用-运算符
}(1234567);

var fn=function(a){
    console.log(a);   //firebug输出12345678,使用=运算符
}(12345678)

()、!、+、-、=等运算符告诉javascript引擎这是一个函数表达式,不是函数声明,可以在后面加括号,并立即执行函数的代码。加括号是最安全的做法,因为!、+、-、=等运算符还会和函数的返回值进行运算,有时造成不必要的麻烦。

五、实例应用

function fn() {
    for(var i=0 ; i<2; i++) {
        var backup = i;
        setTimeout(function() {
            alert(backup);
        }, 2000);
    }
}
fn();

上面代码的for循环一共两次,我们本来的目的是每隔两秒钟把每一次循环的索引i输出,我们期待它应该输出0和1,但是实际输出结果两次都是1。

使用了闭包之后,我们可以把循环中每一次i值的状态都保存下来。

function fn() {
    for(var i=0 ; i<2; i++) {
    (function(){
        var backup = i;
        setTimeout(function() {
            alert(backup);
        }, 2000);
    })();
    }
}
fn();

这次我们得到了正确的结果0和1。

原文地址:https://www.cnblogs.com/camille666/p/function_closure.html