Open lulujianglab opened 6 years ago
闭包问题一直是面试中提到频率最高的问题,那到底什么是闭包呢,百度一下,我们会看到各种层出不穷的答案
所以,闭包是什么呢?
比较权威的答案,在函数内部定义的变量或方法在函数外部被引用时,就形成了闭包
但这种说法还是比较片面的,比如我们在函数内部定义方法,并在函数内部执行,也会形成闭包
红宝书中的定义,闭包是指有权访问另一个函数作用域中的变量的函数
之所以函数能够访问另一个函数作用域中的变量,即使这个内部函数(匿名函数)被返回了,而且是在其他地方被调用了,但它仍然可以访问外部函数中的变量 closure,是因为内部函数的作用域链中包含外部函数的作用域
closure
当某个函数被调用时,会创建一个执行环境及相应的作用域链
《JavaScript权威指南》有提到,函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的
每个函数都有一个活动对象,在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,......直至作为作用域链终点的全局执行环境
在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量
但我觉得红宝书中的说法也不完全正确,我们可以看看开发者工具中的执行结果
所以说,闭包指向的是定义内部函数访问的变量的外部作用域,也可以说,闭包是代码块(函数)和创建该代码块的上下文中数据的结合
官方定义,闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分
我的理解是函数保留了对外部函数变量的引用,简单说就是引用了外部变量,这个变量没有被GC回收,仍存在于函数的作用域链中,这样的函数,叫做闭包
创建闭包的常见方式,就是在一个函数内部创建另一个函数
方式有很多种,比如,可以是在函数内部 return 一个函数(如上),也可以是在函数定义一个内部变量函数,并在函数内部执行(如上),或者是定义一个全局变量函数,在全局执行
或者是在定义一个对象方法,在外部执行
闭包很好地解决了变量过多的问题
我们先来看一个例子
<button>1</button> <button>2</button> <button>3</button>
var buttons = document.querySelectorAll('button') for (var i = 0; i < buttons.length; i++) { !function(index){ buttons[i].onclick = function() { alert(index + 1) } }(i) }
通过立即执行函数形成的闭包很好的解决了button变量的问题,如果没有闭包,我们就需要写多个这样的button点击事件
而且,如果没有闭包,我们以这种方式来定义点击事件,会出现常见的问题
var buttons = document.querySelectorAll('button') for (var i = 0; i < buttons.length; i++) { buttons[i].onclick = function() { console.log(i) } }
这样,每个函数打印的i值其实都是3。因为每个函数的作用域链中都保存着全局的活动对象,所以它们引用的都是同一个变量i。当开始执行 onclick 事件时,变量i的值已经是3了,此时每个对象都引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都是3
onclick
闭包问题一直是面试中提到频率最高的问题,那到底什么是闭包呢,百度一下,我们会看到各种层出不穷的答案
所以,闭包是什么呢?
闭包的定义
比较权威的答案,在函数内部定义的变量或方法在函数外部被引用时,就形成了闭包
但这种说法还是比较片面的,比如我们在函数内部定义方法,并在函数内部执行,也会形成闭包
红宝书中的定义,闭包是指有权访问另一个函数作用域中的变量的函数
之所以函数能够访问另一个函数作用域中的变量,即使这个内部函数(匿名函数)被返回了,而且是在其他地方被调用了,但它仍然可以访问外部函数中的变量
closure
,是因为内部函数的作用域链中包含外部函数的作用域当某个函数被调用时,会创建一个执行环境及相应的作用域链
每个函数都有一个活动对象,在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象处于第三位,......直至作为作用域链终点的全局执行环境
在函数执行过程中,为读取和写入变量的值,就需要在作用域链中查找变量
但我觉得红宝书中的说法也不完全正确,我们可以看看开发者工具中的执行结果
所以说,闭包指向的是定义内部函数访问的变量的外部作用域,也可以说,闭包是代码块(函数)和创建该代码块的上下文中数据的结合
官方定义,闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分
我的理解是函数保留了对外部函数变量的引用,简单说就是引用了外部变量,这个变量没有被GC回收,仍存在于函数的作用域链中,这样的函数,叫做闭包
创建闭包的方式
创建闭包的常见方式,就是在一个函数内部创建另一个函数
方式有很多种,比如,可以是在函数内部 return 一个函数(如上),也可以是在函数定义一个内部变量函数,并在函数内部执行(如上),或者是定义一个全局变量函数,在全局执行
或者是在定义一个对象方法,在外部执行
闭包的作用
闭包很好地解决了变量过多的问题
我们先来看一个例子
通过立即执行函数形成的闭包很好的解决了button变量的问题,如果没有闭包,我们就需要写多个这样的button点击事件
而且,如果没有闭包,我们以这种方式来定义点击事件,会出现常见的问题
这样,每个函数打印的i值其实都是3。因为每个函数的作用域链中都保存着全局的活动对象,所以它们引用的都是同一个变量i。当开始执行
onclick
事件时,变量i的值已经是3了,此时每个对象都引用着保存变量i的同一个变量对象,所以在每个函数内部i的值都是3