最近在重新学习JavaScript,在阅读你不知道的javascript时候,自己整理总结了些内容...所以想分享给大家。在分享的同时,也可以自己学习的更扎实...如果有错误或者理解不正确的地方,麻烦告知我会及时更正。同时也非常欢迎大家一起讨论。鞠躬。 Github地址:https://github.com/bsxz0604/RemarkForYouDontKnowJs (不定期更新...
1.词法作用域定义
分析过程:
作用域1 (绿色) :即全局作用域,包含变量foo;
作用域2 (黄色) :foo函数的作用域,包含变量a,bar,b
作用域3 (蓝色) :bar函数的作用域,包含变量c
bar 作用域里完整的包含了 foo 的作用域, 因为 bar 是定义在 foo 中的,产生嵌套作用域。值得注意的是,一个函数作用域只有可能存在于一个父级作用域中,不会同时存在两个父级作用域。还有诸如this , window , document等全局对象这里就不说了,避免混乱。
执行过程:
语句console.log寻找变量a,b,c;
其中c在自己的作用域中找到,
a,b在自己的作用域中找不到,于是向上级作用域中查找,在foo的作用域中找到,并且调用。
函数在执行时,每遇到一个变量,都会去执行期上下文的作用域链的顶部,也就是执行函数的激活对象开始搜索,如果在第一个作用域链(即,Activation Object 激活对象)中找到了,那么就返回这个变量。如果没有找到,那么继续向下查找,直到找到为止。如果在整个执行期上下文中都没有找到这个变量,在这种情况下,该变量被认为是未定义的。也就是说如果foo的作用域中也定义了c,但bar函数只调用自己作用域里的c。这就是我们说的变量取值。
2.闭包中经典for循环问题
for(var i = 0 ; i<3 ; i++ ){
setTimeout( function (){
console.info(i)
},100)
}
//3
//3
//3
在上面这段代码中,输出的是3个3, 而并非我们想的0 ~ 2
原因:for循环每次注册一个延迟函数, setTimeout是异步的,传入事件队列中,在循环结束后进行处理,当循环结束时,i为3。
setTimeout中的匿名function没有将 i 作为参数传入来固定这个变量的值, 让其保留下来, 而是直接引用了外部作用域中的 i, 因此 i 变化时, 也影响到了匿名function.
所以为了得到我们想要的0~2,这里我们可以利用闭包来完成
for( var i = 0 ; i<3 ; i++ ){
setTimeout(
(function time(j){
return function(){console.info(j)}}
)(i)
,100)
}
//0
//1
//2
当然,在ES6中,我们也可以直接使用let来完成
变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量.
JavaScript 引擎内部会记住上一轮循环的值,初始化每轮的变量i时,就在上一轮循环的基础上进行计算。
for(let i = 0 ; i<3 ; i++ ){
setTimeout( function (){
console.info(i)
},100)
}
//0
//1
//2
ps: for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
for(let i = 0 ; i<3 ; i++ ){
setTimeout( function (){
let i = 'abc';
console.log(i);
},100)
}
//abc
//abc
//abc