面试基本都会问到的问题,面试题基本都会有的一项问题
1、例子
产品一
产品二
产品三
产品四
产品五
(1) 需求 : 每次点击对应目标时弹出对应的index( 0~4)
function onMyLoad(){
var arr = document.querySelectorAll('p')
for(var i = 0, len = arr.length; i < len; i++) {
arr[i].onclick = function(){
alert(i)
}
}
}
执行上面的代码,你会发现每次弹出的都是5。为什么会出现这种问题?
原因是i是全局变量,在你点击任意一项的时候,i的值已经变为了5,由于i是全局变量,所以,所有弹出的都是5
2、解决方案
- 方案一 : 使用let 声明变量
for(let i = 0; i < arr.length;i++){
arr[i].onclick = function(){
alert(i)
}
}
原理 : 变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算(参见es6阮一峰)。本质就是 let 声明的变量具有块级作用域
- 方案二 : 立即执行函数+闭包
for(var i = 0; i < arr.length;i++){
arr[i].onclick = (function(){
var j = i
return function() {
alert(j)
}
}())
}
for(var i = 0; i < arr.length;i++){
arr[i].onclick = (function(i){
return function() {
alert(i)
}
}(i))
}
for(var i = 0; i < arr.length;i++){
(function(i) {
arr[i].onclick = function(){
alert(i)
}
}(i))
}
for(var i = 0; i < arr.length;i++){
(function() {
var j = i;
arr[i].onclick = function(){
alert(j)
}
}())
}
原理 : 上边的四种方法其实就是一种方法。在一个函数中返回另一个函数,并且返回的函数中引用到了父函数作用域中的变量,则该变量不会被自动回收。外部函数还有对此变量的引用。(语言表达能力欠佳,可以参考犀牛书闭包章节,讲的很清楚)
- 方案三 : 单独设置属性
for(var i = 0; i < arr.length;i++){
arr[i].i = i;
arr[i].onclick = function(){
alert(this.i)
}
}
原理 : 为数组的每个元素单独设置对应的属性,此属性是唯一的,不会再是全局变量
- 方案四 : 通过bind()方法实现
function f(i) {
alert(i)
}
for(var i = 0; i < arr.length;i++){
arr[i].onclick = f.bind(null,i)
}
原理 : 通过f.bind(),将f包装返回一个新的函数。 (解释的不行,其实还是闭包的应用,因为bind()方法就是使用闭包实现的)参见下面bind()简单的实现,你就明白了: 在执行f.bind(null, i)的时候,参数传入了闭包函数中,并且返回了一个函数,且返回的这个函数引用到了父函数中的boundArgs(这里面就包含了当前的i)变量,因此在返回函数后,此变量不会被垃圾回收机制回收,在返回的函数中还可以正常引用到boundArgs(这里面就包含了当前的i)。
Function.prototype.fbind = function(obj, /*args*/) {
let self = this,
boundArgs = arguments;
return function() {
let args = [],
i;
for(i = 1, len = boundArgs.length; i < len; i++){args.push(boundArgs[i])};
for(i = 0, len = arguments.length; i < len; i++) {args.push(arguments[i])};
return self.apply(obj, args)
}
}
- 方案五 : 通过Function实现(用到的不多)
for(var i = 0;i
解释一下上面函数 : 通过 new 使用 Function 的构造函数 创建 Function 实例实现,由于传入的函数体的内容是字符串,故 Function 得到的是一个字符串拷贝,而没有得到 i 的引用(这里是先获取 i.toString()然后与前后字符串拼接成一个新的字符串,Function 对其进行反向解析成 JS 代码
3、 补充一个常见的面试题
//方案一
for (var i = 0; i < 5; i++) {
setTimeout( (function timer(i) {
return function() {
console.log(i);
}
}(i)), 0 );
}
//方案二
for (var i = 0; i < 5; i++) {
(function(i) {
setTimeout( function() {
console.log(i);
}, 1000);
}(i))
}
......
4、补充一个常见的面试题
Helpful notes will appear here
E-mail:
Name:
Age: