最近想梳理一下自己的知识脉络,准备用博文记录一下学习前端以来获取到的各种杂七杂八的知识体系。
就先从一些微小的方面入手,然后再慢点做一下扩展。
复习 js 高程的时候又看到了经典的闭包,里面有个小例子用来演示:
function createFunction() {
var result = new Array()
for (var i = 0; i < 10; i++) {
result[i] = function() {
return i
}
}
for (var j = 0; j < result.length; j++) {
result[j] = result[j]()
}
return result
}
console.log(createFunction().join(' '))
这是个很简单的问题,初学者应该都能很轻松的答上来,最后打出来是:
10 10 10 10 10 10 10 10 10 10
var 的 bug
其实这是 var 的一个 bug。在 for 循环中,如果存在一个未在这个循环进行时调用的函数,并且这个函数内部使用了 var 定义的 i 变量,由于函数未被调用,里面的 i 值我们在循环时是不知道的,只有在函数被调用的情况下这个 i 值才会被确定。
回到上面这个例子,匿名函数调用时打印出来的 i 全部都是 10,有理由相信,这 10 个匿名函数里的 i 全都指向栈内存中的同一个值,这个值就是在 for 循环中 var 定义的 i,每次循环 i 的值都会变,最后变为 10,所以最后打印出来的也全部都是 10。
let 的不同之处及猜想
但是 let 不同,再来看一下:
function createFunction() {
var result = new Array()
for (let i = 0; i < 10; i++) {
result[i] = function() {
return i
}
}
for (let j = 0; j < result.length; j++) {
result[j] = result[j]()
}
return result
}
console.log(createFunction().join(' ')) // 0 1 2 3 4 5 6 7 8 9
打印出的是 0~9,这应该才是符合我们常识的输出结果,那这是什么原因呢?
看到网上很多人说这是因为 let 在块级作用域中生效,而 var 在函数作用域中生效。但是这个原因跟上面的例子好像没有什么必然联系,因为在匿名函数被调用时早就脱离了上面的 for 循环块。
猜想可能是在 for 循环中 let 定义的变量 i 在每次进入循环都会创建一个副本,而进入下一个循环之前会回收这个副本,但是由于匿名函数形成闭包,副本 i 回收不了所以最后调用匿名函数打印出来的值也不同,因为每个匿名函数中的 i 都是不同状态下 i 的副本。
let 和 var 的其他不同之处就不再赘述。上述的猜想其实还是闭包引出的,没有找到可以查看 js 运行时栈内存具体 key-value 的工具,暂时证明不了副本之说,以后能证明了会补上。
这篇文章引出了闭包,这是个很大的知识点,之后几篇再来搞定它。
网友评论