从JS执行过程分析闭包的概念

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/w1857518575/article/details/83090648

(自己理解,有误请指正 )

首先,闭包这个词真让人难理解,中华文化博大精深,讲究见名知意,但是闭包确实是理解一门语言特性中不可或缺的一个东西,网络上资料千奇百怪,讲了一通也很难让人有醍醐灌顶的感觉,这里我来说一说我对闭包的理解:

# 闭包是和特定语言无关的东西

# 闭包是指的是通过特定方式允许跨作用域访问变量

# 先分析下常规的变量作用域:

首先,人类编程语言基本分为两种,一种是函数式的编程语言,一种是面向对象的编程语言;

对于函数式语言,以函数为执行单位,一个变量声明在一个函数里,只能作为局部变量,变量作用域只在本函数体内部,另外一个函数不允许访问本函数内部的局部变量,例如:

void a(){ int i = 1; }

void b(){ println(a.i); }

b函数是不允许访问a函数中的变量的,实际上,在函数最终执行过程中,每执行一个函数就会有一个栈帧压入堆栈,函数中的局部变量是放在本函数的私有栈帧中的,执行另一个函数的时候会压入一个新的栈帧,每当函数执行完毕后栈帧就会消除,不支持闭包的语言体现在不允许跨栈访问变量。

对于面向对象的语言,不允许跨作用域访问体现在一个类对象不能访问另一个类对象的私有成员变量。

扫描二维码关注公众号,回复: 4668806 查看本文章

# 支持闭包的语言的变量作用域是啥样的呢?

函数式编程语言举一个JS闭包的例子

function outer() {
     var  a = '变量1'
     var  inner = function () {
            console.info(a)
     }
    return inner    // inner 就是一个闭包函数,因为他能够访问到outer函数的作用域
}

外部调用var bb = outer(); bb();这样就能在控制台打印出outer函数作用域内部的变量的a的值。

面向对象的编程语言的闭包的例子就是java的内部类,体现在外部调用一个类的内部类对象的方法能够访问外部类对象的私有成员变量。

 # 从JS的执行过程来分析JS的闭包是如何实现的

JS是个解释型语言,解释引擎在加载了一个JS源文件后,首先进行词法分析,分词后得token信息,然后构建抽象语法树,然后开始根据抽象语法树开始解释执行代码。js一个函数解释执行过程中会对应一个运行上下文,每调用一个函数就会生成一个运行上下文并压入运行上下文堆栈,函数运行上下文中有一个活动对象,函数的参数、函数内部定义的变量、函数内部定义的嵌套函数等都会作为属性挂载到这个活动对象上。分析下面的例子:

function a(){
    var i = 0;
    function b(){
        alert(i);
    }
    return b;
}

var fun = a();
fun();

解释器执行到函数a时,在内存中为函数a分配执行上下文,并压入执行上下文堆栈,执行上下文中包含当前函数的活动对象;

分别将a函数体内声明的变量i和函数b作为属性保存到当前上下文的活动对象中;

执行函数返回操作,把函数b的引用返回给外层函数定义的变量fun,然后销毁当前函数的执行上下文,但是由于外层持有了本函数的活动对象中的变量,本函数执行上下文中的活动变量并不会被销毁;

执行fun函数,此时的fun就是a的内部函数b的一个引用,解释器为该函数分配新的执行上下文并入栈,这里注意:由于函数b是定义在函数a中的活动对象中的,此时在函数b的执行上下文中的活动对象中会包含一个指向a函数的活动对象的引用,这样两个函数的活动对象就串了起来,组成一个变量作用域链。解释器在执行函数b的alert(i)语句时,会先在b函数自己的活动对象中找有没有变量i的定义,如果没有的化就会沿着作用域链查找,从上层的a函数的活动对象中找到了变量i的定义,于是输出了i的值;

通过上面描述的方式,达到了外层函数访问函数a中定义的私有变量i的目标--这就是所谓的闭包。

猜你喜欢

转载自blog.csdn.net/w1857518575/article/details/83090648
今日推荐