javascript函数作用域及闭包
javascript中作用域链应该是比较抽象的,因为它有别于原型链,作用域链在执行环境和非执行环境是不同的。函数在声明的时候,会创建一个作用域链。此时的作用域链有两部分,一部分是全局作用域,另一部分是这个函数可以访问到的其他作用域(此时没有包含自身作用域的变量对象)这个作用域链被保存在[[scope]]属性中。当这个函数执行的时候。会创建执行环境,复制[[scope]]中的对象建立作用域链,并且添加当前作用域的变量对象到作用域链的前端。把完整的作用域链丢到执行环境。
语言描述不够直观,在chrome调试器里给大家演示一下
function one() { var a = 'a' console.log(a) function two() { var b = 'b' console.log(a) } two() } one()
在浏览器里可以看到,当函数声明之后,里面会有一个scopes属性,里面保存了除了内部变量对象之外的所有可以访问到的作用域的变量对象。
当函数执行时又是怎么样的呢? 我们来看代码执行到 two函数内部的情况
图片已经标注啦,就不啰嗦啦。
下面我们来讲一下闭包
闭包是指有权访问另一个函数作用域中的变量的函数
通过函数作用域链,内部函数是可以访问外部函数的变量对象的,这比较好理解,上面two函数就是one函数的闭包。
但是闭包的一个常用场景
var arr = [ { name:'Alan', age:12 }, { name:'Nicholas', age:43 }, { name:'Bob', age:34 },{ name:'Linda', age:18 } ] function createComparisonFunction(propertyName) { return function (obj1,obj2) { // 这里返回一个匿名函数 (顺便提一下匿名函数this默认是window) var value1 = obj1[propertyName] var value2 = obj2[propertyName] if(value1 > value2){ return 1 }else if(value1 == value2){ return 0 }else{ return -1 } } }
var transfer = createComparisonFunction('age')
var newArr = arr.sort(transfer) console.log(newArr)
我们经常会遇到上面这种情况,遍历一个每一项是对象的数组。这里sort方法,并不是createparsionFunction的内部函数。如果没有return一个匿名函数的话,是不可以访问createparsionFunction
的内部变量propertyName的。但是这里返回了一个匿名函数,使外部函数有权访问内部变量。但是这样做有一个问题,正常情况想js垃圾回收是会在一个函数脱离执行环境之后进行回收的,但是这里作用域链被外部引用了,垃圾回收是不会回收这条作用域链上的变量对象的。(提一下js的垃圾清理机制,js的垃圾回收常用标记清除,垃圾回收器在运行的时候会给存储在内存中的所有变量加上标记,去掉环境中的变量和被环境中变量引用的变量的标记。最后将标记的变量清除)。如果有很多这种不被清除的变量,就会造成内存泄漏问题啦