腾讯js面试题3
该题难度系数: ★★★
考查的技术点: 1). DOM事件处理 2). 闭包
题目如下:
/*
需求: 点击某个按钮, 提示"第n个"
问题: 下面的实现代码是否正确, 如果不正确, 请正确实现
*/
var btns = document.getElementsByTagName('button')
for(var i=0,length=btns.length;i<length;i++) {
var btn = btns[i];
btn.onclick = function () {
alert('第'+(i+1)+'个');
}
}
单击每个按钮得到的运行结果均如图所示:
根据结果,这明显是错误的啊。
那为什么会这样子呢?这明显不是我们想要的结果啊。因为i是一个全局变量(函数级变量),而3个内部函数都指向了同一个i,且最后一次i的赋值是3,所以点击弹窗时就是用同一个变量的。
那么接下来,我们该如何修改这段代码呢?目前想到几种方法:
第一种,用let代替var;
就是在for循环体里,把var换成let即可,如下所示:
var btns = document.getElementsByTagName('button')
for(let i=0,length=btns.length;i<length;i++) {
var btn = btns[i]
btn.onclick = function () {
alert('第'+(i+1)+'个')
}
}
此时的结果就是我们要的结果了:
那原理是什么呢?根据官方文档得知let就是声明一个块范围变量,即该变量的范围限于声明它的块中。就是let 允许把变量的作用域限制在块级域中,而var 申明变量要么是全局的,要么是函数级的,而无法是块级的,这就是let的精妙之处。i 变成块级域(也就是花括号中的块,每进入一次for的花括号就生成了一个块级域),所以3个内部函数指向了不同的i。
第二种,使用闭包。
那什么是闭包呢?一般两个条件,一是函数嵌套函数,二是函数可以访问另外一个函数的局部变量。简言之,有权访问另一个函数作用域内变量的函数都是闭包,这样就形成了一个闭包。
先来看个小案例:
function fn1() {
var a = 2
function fn2 () {
a++
console.log(a)
}
return fn2
}
var f = fn1()
f()
f()
结果如下:
分析如下:
我们在浏览器debug调试一下这段代码,刚开始闭包里面a为2,closure就是闭包的意思。
这时候输出的是3了,
再接下来,就是输出4了
这说明了,闭包可以实现局部变量的累加。但是要注意一点,不必要的闭包只会徒增内存消耗。
好的,言归正传,我们再来看下这道题目,用闭包如何实现,代码如下所示:
var btns = document.getElementsByTagName('button')
for(var i=0,length=btns.length;i<length;i++) {
(function (i) {
var btn = btns[i]
btn.onclick = function () {
alert('第'+(i+1)+'个')
}
})(i)
}
得到的是正确的结果:
原因是加了闭包之后,内存里面新增了3个不同的i,确切的说是4个,因为for里面也有一个i,3个内部函数指向了不同的i。
还没有评论,来说两句吧...