什么是作⽤域链?
function bar() {
console.log(myName)
}
function foo() {
var myName = "极客邦"
bar()
}
var myName = "极客时间"
foo()

作⽤域链
在每个执⾏上下⽂的变量环境中,都包含了⼀个外部引⽤,⽤来指向外部的执⾏上下⽂,我们把这个外 部引⽤称为outer。
当⼀段代码使⽤了⼀个变量时,JavaScript引擎⾸先会在“当前的执⾏上下⽂”中查找该变量
如果在当前的变量环境中没有查找到,那么JavaScript引擎会继 续在outer所指向的执⾏上下⽂中查找。

bar函数和foo函数的outer都是指向全局上下⽂的
JavaScript引擎会去全局执⾏上下⽂中查找。我们把这个查找的链条就称为作⽤域链。
foo函数调⽤的bar函数,那为什 么bar函数的外部引⽤是全局执⾏上下⽂,⽽不是foo函数的执⾏上下⽂?
foo和bar的上级作⽤域都是全局作⽤域(定义为函数的时候绝对,如果把bar定义在foo里面,那么bar的上级作用域就指向foo),所以如果foo或者bar函数使⽤了⼀个它 们没有定义的变量,那么它们会到全局作⽤域去查找
词法作⽤域是代码阶段就决定好的,和函数是怎么调⽤的没有关系。
词法作⽤域
词法作⽤域就是指作⽤域是由代码中函数声明的位置来决定的,所以词法作⽤域是静态的作⽤域,通过它就 能够预测代码在执⾏过程中如何查找标识符。

词法作⽤域就是根据代码的位置来决定的,其中main函数包含了bar函数,bar函数中包 含了foo函数
因为JavaScript作⽤域链是由词法作⽤域决定的,所以整个词法作⽤域链的顺序是:foo函数 作⽤域—>bar函数作⽤域—>main函数作⽤域—>全局作⽤域。
闭包
function foo() {
var myName = "极客时间"
let test1 = 1
const test2 = 2
var innerBar = {
getName: function () {
console.log(test1)
return myName
},
setName: function (newName) {
myName = newName
}
}
return innerBar
}
var bar = foo()
bar.setName("极客邦")
bar.getName()
console.log(bar.getName())
当执⾏到foo函数内部的return innerBar这⾏代码时调⽤栈的情况

根据词法作⽤域的规则,内部函数getName和setName总是可以访问它们的外部函数foo中的变量
虽然foo函数已经执⾏结束,但是getName和setName函数依然可 以使⽤foo函数中的变量myName和test1

foo函数执⾏完成之后,其执⾏上下⽂从栈顶弹出了,但是由于返回的setName和 getName⽅法中使⽤了foo函数内部的变量myName和test1,所以这两个变量依然保存在内存中。
内部函数 总是可以访问其外部函数中声明的变量,即使该外部函数已 经执⾏结束了,但是内部函数引⽤外部函数的变量依然保存在内存中,我们就把这些变量的集合称为闭包。
闭包是怎么回收的
因为如果闭包使⽤不正确,会很容易造成内存泄漏的
如果引⽤闭包的函数是个局部变量,等函数销毁后,在下次JavaScript引擎执⾏垃圾回收时,判断闭包这块 内容如果已经不再被使⽤了,那么JavaScript引擎的垃圾回收器就会回收这块内存。
思考题
var bar = {
myName:"time.geekbang.com",
printName: function () {
console.log(myName)
}
}
function foo() {
let myName = "极客时间"
return bar.printName
}
let myName = "极客邦"
let _printName = foo()
_printName()
bar.printName()
打印两次 极客邦
bar 不是⼀个函数,因此 bar 当中的 printName 其实是⼀个全局声明的函数
bar 当中的 myName 只 是对象的⼀个属性,也和 printName 没有联系,如果要产⽣联系,需要使⽤ this 关键字
printName函数定义时的执⾏上下⽂是全局,所以会在全局词法环境和变量环境下找myName。
foo 函数返回的 printName 是全局声明的函数,因此和 foo 当中定义的变量也没有任何联系,这个时 候 foo 函数返回 printName 并不会产⽣闭包