ECMAScriptJavaScript中的效用域和闭包

率先肯定安利《你不明白的JavaScript》,JS初学者进阶必读。

对于从C++、Java等静态语言转向JavaScript的初学者(比如我)来说,JS一些奇异而又特别匆忙的特性使得它显得万分奇幻而难以捉摸,为此必须下一番大力气,一边啃书一边实践将那个概念彻底搞懂,然后才谈得上进一步学习前端姿势。(注:本文里的JS以ECMAScript
5(ES5)为准,ES6的新特色我也是刚刚接触,希望未来能与我们一块儿上学商量。)

深谙Java的童鞋在初学JS时,必须要切记一点:

JS中没有块级功效域!

{
    var test=10;
}
console.log(test);    // 控制台输出:10

纳尼?是不是看着很别扭?还有更坑爹的:

var obj={
    test:10,
    myFunc:function(){
        console.log(test); 
    }
};
obj.myfunc();   // 出错,或者IDE直接报警了

同一个对象,自己的函数都不认自己的属性了?

还真是。

对此初学者而言,可以这样认为:除了全局功用域之外,JS只有一种作用域,即函数功效域。(try
catch{}语句也得以定义功效域,不过最近截至我还没实际用过)

也就是说,写在函数体内的变量,只要不是嵌套在更深层的函数里,就是处在同一效用域的,“互相可见”;而其它的花括号,不管是for后跟的,if后跟的,仍然对象字面量的,一概“不作数”,起不到定义效能域的功用,变量阐明写在那么些花括号的里边或外面都平等。

那位于嵌套的函数里的效率域呢?它们有着“单向透明”的特权,即:在较内层次的效益域内可以访问较外层次意义域里的变量,反之则充裕。

function outerFunc(){
    for(var i=0;i<10;i++){doSomething;}
    console.log(i); // 控制台输出10,因为i位于outerFunc的作用域
    var outer = 10;
    function innerFunc(){
        var inner = outer; // 内层作用域可以访问外层作用域里的变量
    }
    console.log(inner); // 报错,外层作用域访问不到内层作用域里的变量
}

再来分析上一个事例。我们准备在myFunc的功用域内部访问test,但是test并不是一个“与myFunc位于同一个对象效用域”的变量,事实上根本不设有“对象功用域”这回事,test是obj的一个属性,不是一个“独立”的变量,要拜访test只好由此点运算符obj.test或obj[“test”],哪怕是在myFunc内部。当然,myFunc内部可以访问到obj这些位于外层成效域的变量,没有问题。于是将代码改写如下:

var obj={
    test:10,
    myFunc:function(){
        console.log(obj.test); 
    }
};
obj.myfunc();   // 10

既然如此在内层效率域里可以访问外层功用域,那么就生出了一个好玩的面貌,叫做“闭包”。创立一个闭包只需要两步:

1.在内层函数里引用外层函数的变量

2.将内层函数作为外层函数的重返值重返出去

function outer(){
    var test = 10;
    var inner = function(){
        console.log(test++);
    };
    return inner;
}

var myFunc = outer(); // 将outer的返回值(inner函数)赋给myFunc
myFunc(); // 10
myFunc(); // 11
myFunc(); // 12

本条被重临的inner函数就是一个闭包。即使outer函数运行截止了,但它的中间变量test因为被闭包引用,所以并不曾被灭绝,而是被保留了四起,并且可以通过闭包继续操作。当然,外界永远不可以访问test这多少个变量,它成了inner(以及myFunc)所指向的函数的“私有变量”。