javaScript----图解作用域链的执行环境来初识闭包

212 阅读5分钟

前言

关于作用域链和闭包,相信大家并不陌生,对于作用域链和闭包的联系,关于面试题常考的知识点闭包与变量的使用,大家有何自己的见解~一起来交流交流,下面是我在阅读红宝书第七章时,对作用域链的一个自己的理解!

知识点先知

  • 作用域的执行环境
  • 闭包初识
  • 闭包与变量(面试题常考点)

图解作用域的执行环境

有没有这样的疑惑,理解闭包需要把作用域链的细节理解清楚,答案就在这里,先看一段代码吧。

function compare(value1,value2){
    if(value1 < value2){
        return -1;
    }else if(value1 > value2){
        return 1;
    }else{
        return 0;
    }
}
var result = compare(15,10);
console.log(result);    // 输出 1

初看这段代码,相信大家会和我一样,认真阅读,捋顺自己的逻辑思路,然后会很准确的在心里默念出console的答案:10。那有没有想过为什么呢?他是怎样的运行机制呢?

初步分析

首先,compare函数在全局被调用,就会创建出一个执行环境,然后通过var result = compare(15,10)传递参数变量初始化这个函数,这也可以称为函数的活动对象;接着这个活动对象就开始在当前函数的作用域链中查找执行读取这个变量。

注意:在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位...一直这样一级一级的查,直到终点全局执行环境下。在上边代码中,处于作用域链第二位的是全局执行环境下的变量对象(result和compare);

深入理解

那在当前作用域下,后台的执行环境又是怎么样的呢?接下来呢,是我按照自己的一个理解和习惯,图解了一番,虽不太美观,但还是希望能帮助大家一起理解作用域链的执行环境。

是不是类似看图说话,初看,好像很乱糟糟,我来注释一番,帮大家看懂图,首先呢大家把目光放在详细部分,开篇是这样的,在每个后台执行环境中都会有表示变量的对象(称作变量对象),包括如图所示的全局变量对象局部变量对象;灰色箭头表示作用域链基本的执行顺序,创建时-调用时-执行-执行完毕;这样是不是就清楚些了~也许到这儿可能有小伙伴又会问,说这些和闭包有啥联系?那就继续和小编一起看下边吧。

闭包初识

那闭包究竟是什么呢?红宝书(P178)是这样定义的:闭包是指有权访问另一个函数作用域中的变量的函数,我们换个说法:就是如果一个函数引用了局部环境中的一个对象,并且在这个函数中把这个对象返回到了更高层的环境中,那么,这个函数就是闭包。下边是一个简单的创建闭包的例子,可以更形象的说明一番,来一起看看吧。

function user(){
    var name =  "mangguo";
    return function getName(){ 
        return name;
    }
}
var userName = user()(); 
console.log(userName);  // 输出 mangguo
userName = null;    //销毁闭包,释放内存 

这部分代码中就存在闭包,根据定义很好确认getName 就是闭包,那深究一下,具体是为什么呢?大家可以这样理解,全局作用域下的userName是老大;他呢引用着局部作用域下的user(),这是老二;这老二里边又返回了一个函数,这个函数还返回出去了name,这是老三;由于老三在老二里,老大又牵连着老二,这就相当于把老三(这个局部环境中的变量)放到了老大(这个最高层的环境中引用);这样的引用就可以称作是闭包~大家可以好好理解一下。

闭包与变量

关于闭包与变量,刚看到这一部分,我就想到了之前面试包括现在我朋友面试的时候很常见的一个面试题,一起看一下这个面试题来思考思考。

function a(){
    var arg = [];
    for(var i = 0; i < 10; i++){
        arg[i] = function(){
                return i;
            };
    }
    return arg;
}
console.log(a()[1]());  // 1--输出 ?
function a(){
    var arg = [];
    for(var i = 0; i < 10; i++){
        arg[i] = function(num){
            return function(){
                return num;
            };
        }(i)
    }
    return arg;
}
console.log(a()[1]()); // 2--输出?

经过闭包初识,相信大家能够看出来吧,这里也包含着一个闭包函数,那这个两段代码会输出什么呢?是10还是对应下标呢?大家可以试试看~来试着分析一下,仔细看,利用老大,老二,老三的关系,我们可以找到arg[i]这个函数就是一个闭包,那么仔细注意到,他们一个返回的是变量i,另一个他没有直接把闭包赋值给数组而是定义了一个匿名函数里边还传递了一个参数num,这样的话输出是不是就和第一段代码有差别呢?到这里相信小伙伴心里已经有了答案。(滑到最后,有答案)

注意: 闭包所保存的是整个的变量对象,而不是某个特殊的变量;函数参数是按传递的;

小结

关于作用域链啊、闭包啊这些在js里常见到的侠客,他们深剖进去,你还会发现很多很多新鲜的知识点,每次查看了解都会有不一样的收获和见解;这篇文章只是一个初识,(1--输出10;2--输出1)希望能够在你理解他们的过程中帮助到你,一起加油~