We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
function bar() { console.log(myName) } function foo() { var myName = "极客邦" bar() } var myName = "极客时间" foo()
根据执行上下文我们可以知道:
此时的调用栈是这样的。
那么 console.log(myName) 会打印什么值呢?答案是 "极客时间",而不是 "极客邦"。为什么不是从调用栈里自下而上的找变量,而是直接使用了全局执行上下文的变量呢?因为其实在变量环境中,又一个外部引用 outer,他来指定当前上下文的外部的执行上下文。
console.log(myName)
这个,就是我们经常提到的作用域链了。
作用域链就是查找变量的这个一条链路。
那为什么 bar函数的 outer 会指向全局执行上下文,而不是 foo 的函数执行上下文呢?因为这个作用域链是有词法作用域决定的。
bar
outer
foo
JS 中使用的是词法作用域。
词法作用域规定作用域是在函数定义时(代码编译阶段)的位置确定的,而不是执行时确定的。
所以 bar 函数虽然是在 foo中被执行的,但是 bar是在全局作用域下定义的,所以outer 指向了 全局执行上下文。
function bar() { var myName = "极客世界" let test1 = 100 if (1) { let myName = "Chrome浏览器" console.log(test) } } function foo() { var myName = "极客邦" let test = 2 { let test = 3 bar() } } var myName = "极客时间" let myAge = 10 let test = 1 foo()
这里,你肯定可以知道打印出来的是 1
1
古典的闭包:从闭包最开始的来源:《The mechanical evaluation of expressions》中,可以看到闭包有两部分组成:
对应到 js 就是:
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 中的变量,所以当 innerBar 对象返回给全局变量 bar 时,虽然 foo 函数已经执行结束,但是 getName 和 setName 函数依然可以使用 foo 函数中的变量 myName 和 test1。所以当 foo 函数执行完成之后,其整个调用栈的状态如下图所示:
从上图可以看出,foo 函数执行完成之后,其执行上下文从栈顶弹出了,但是由于返回的 setName 和 getName 方法中使用了 foo 函数内部的变量 myName 和 test1,所以这两个变量依然保存在内存中。
出来当函数可以记住并且访问所在的词法作用域。就产生了闭包。就算函数是在当前的词法作用域之外执行。
完整的执行上下文包括了 「变量环境」「词法环境」「outer」「this」
文中没有提到 this,关于 this 的指向都已经知道了,就没有再记录了。向彻底知道 this 的话,要去翻看 ECMA 的文档了,每次都对 this 的定义做了修改
The text was updated successfully, but these errors were encountered:
No branches or pull requests
变量的查找 —— 作用域链与闭包
根据执行上下文我们可以知道:
此时的调用栈是这样的。
那么
console.log(myName)
会打印什么值呢?答案是 "极客时间",而不是 "极客邦"。为什么不是从调用栈里自下而上的找变量,而是直接使用了全局执行上下文的变量呢?因为其实在变量环境中,又一个外部引用 outer,他来指定当前上下文的外部的执行上下文。这个,就是我们经常提到的作用域链了。
作用域链
作用域链就是查找变量的这个一条链路。
那为什么
bar
函数的outer
会指向全局执行上下文,而不是foo
的函数执行上下文呢?因为这个作用域链是有词法作用域决定的。词法作用域
JS 中使用的是词法作用域。
词法作用域规定作用域是在函数定义时(代码编译阶段)的位置确定的,而不是执行时确定的。
所以
bar
函数虽然是在foo
中被执行的,但是bar
是在全局作用域下定义的,所以outer
指向了 全局执行上下文。块级作用域中的变量查找
这里,你肯定可以知道打印出来的是
1
闭包
闭包都是由哪些组成的呢
古典的闭包:从闭包最开始的来源:《The mechanical evaluation of expressions》中,可以看到闭包有两部分组成:
对应到 js 就是:
结合词法作用域了解闭包
首先我们看看当执行到 foo 函数内部的return innerBar这行代码时调用栈的情况,你可以参考下图:
根据词法作用域的规则,内部函数 getName 和 setName 总是可以访问它们的外部函数 foo 中的变量,所以当 innerBar 对象返回给全局变量 bar 时,虽然 foo 函数已经执行结束,但是 getName 和 setName 函数依然可以使用 foo 函数中的变量 myName 和 test1。所以当 foo 函数执行完成之后,其整个调用栈的状态如下图所示:
从上图可以看出,foo 函数执行完成之后,其执行上下文从栈顶弹出了,但是由于返回的 setName 和 getName 方法中使用了 foo 函数内部的变量 myName 和 test1,所以这两个变量依然保存在内存中。
所以什么是闭包呢?
出来当函数可以记住并且访问所在的词法作用域。就产生了闭包。就算函数是在当前的词法作用域之外执行。
最后补充一点,完整的图
完整的执行上下文包括了 「变量环境」「词法环境」「outer」「this」
文中没有提到 this,关于 this 的指向都已经知道了,就没有再记录了。向彻底知道 this 的话,要去翻看 ECMA 的文档了,每次都对 this 的定义做了修改
参考:
The text was updated successfully, but these errors were encountered: