循环与闭包
在一次面试中,面试官出了这样一道题,让我说出运行结果
1 | for(var i=1;i<=5;i++){ |
这段代码在运行时会以每秒一次的频率输出五次 6。
首先,延迟函数的回调会在循环结束时才执行(具体内容可以了解一下事件循环)。根据作用域的工作原理,它们都被封闭在一个共享的全局作用域中,因此实际上只有一个 i
,所有函数共享一个 i
的引用。
可以按照下面来改进:
1 | for (var i=1; i<=5; i++) { |
IIFE
(立即调用函数表达式)会为每个迭代都生成一个新的作用域,使得延迟函数的回调可以将新的 作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。
如果你知道ES6的let
,特点是具有块作用域,for
循环的let
每次迭代都会用上一次迭代结束的值初始化这个变量,所以还可以这样写:
1 | for (let i=1; i<=5; i++) { |
普通匿名函数和箭头函数的this
this
对象是在运行时基于函数的执行环境绑定的:在全局函数中,this
等于 window
,而当函数被作为某个对象的方法调用时,this
等于那个对象。绑定this
指向等问题这里不做讨论。
在匿名函数中使用 this
对象也可能会导致一些问题。匿名函数的执行环境具有全局性,其this
对象通常指向window
,而在严格模式下则是undifined
。
1 | var name = "The Window"; |
箭头函数的函数体内的this
对象,就是定义时所在的对象,而不是使用时所在的对象。箭头函数可以让setTimeout
里面的this
,绑定定义时所在的作用域
1 | function foo() { |
如果是普通函数,执行时this
应该指向全局对象window
,这时应该输出21
1 | function foo() { |
箭头函数的this
就是外层作用域的this
,所以它的this
指向是相对固定化的。