for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 0); console.log(i); }
结果是:0 1 2 3 3 3
很多公司面试都爱出这道题,此题考察的知识点还是蛮多的。
为了防止初学者栽在此问题上,此文稍微分析一下。
都考察了那些知识点呢?
异步、作用域、闭包,你没听错,是闭包。
我们来简化此题:
setTimeout(function() { console.log(1); }, 0); console.log(2);
先打印2,后打印1。
因为是setTimeout是异步的。
正确的理解setTimeout的方式(注册事件):
有两个参数,第一个参数是函数,第二参数是时间值。
调用setTimeout时,把函数参数,放到事件队列中。等主程序运行完,再调用。
没啥不好理解的。就像我们给按钮绑定事件一样:
btn.onclick = function() { alert(1); };
这么写完,会弹出1吗。不会!!只是绑定事件而已!
必须等我们去触发事件,比如去点击这个按钮,才会弹出1。
setTimeout也是这样的!只是绑定事件,等主程序运行完毕后,再去调用。
setTimeout的时间值是怎么回事呢?
比如:
setTimeout(fn, 2000)
我们可以理解为2000之后,再放入事件队列中,如果此时队列为空,那么就直接调用fn。如果前面还有其他的事件,那就等待。
因此setTimeout是一个约会从来都不准时的童鞋。
继续看:
setTimeout(function() { console.log(i); }, 0); var i = 1;
程序会不会报错?
不会!而且还会准确得打印1。
为什么?
因为真正去执行console.log(i)这句代码时,var i = 1已经执行完毕了!
所以我们进行dom操作。可以先绑定事件,然后再去写其他逻辑。
window.onload = function() { fn(); } var fn = function() { alert('hello') };
这么写,完全是可以的。因为异步!
es5中是没有块级作用域的
for (var i = 0; i < 3; i++) {} console.log(i);
也就说i可以在for循环体外访问到。所以是没有块级作用域。
但此问题在es6里终结了,因为es6,发明了let。
这回我们再来看看原题。
原题使用了for循环。循环的本质是干嘛的?
是为了方便我们程序员,少写重复代码。
让我们倒退50年,原题等价于:
var i = 0; setTimeout(function() { console.log(i); }, 0); console.log(i); i++; setTimeout(function() { console.log(i); }, 0); console.log(i); i++; setTimeout(function() { console.log(i); }, 0); console.log(i); i++;
因为setTimeout是注册事件。根据前面的讨论,可以都放在后面。
原题又等价于如下的写法:
var i = 0; console.log(i); i++; console.log(i); i++; console.log(i); i++; setTimeout(function() { console.log(i); }, 0); setTimeout(function() { console.log(i); }, 0); setTimeout(function() { console.log(i); }, 0);
这回你明白了为啥结果是0 1 2 3 3 3了吧。
那个,说它是闭包,又是怎么回事?
为了很好的说明白这个事情,我们把它放到一个函数中:
var fn = function() { for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 0); console.log(i); } }; fn();
上面的函数跟我们常见另一个例子(div绑定事件)有什么区别:
var fn = function() { var divs = document.querySelectorAll('div'); for (var i = 0; i < 3; i++) { divs[i].onclick = function() { alert(i); }; } }; fn();
点击每个div都会弹出3。道理是一样的。因为alert(i)中的i是fn作用越中的,因而这是闭包。
《javascript忍者秘籍》书里把一个函数能调用全局变量,也称闭包。
因为作者认为全局环境也可以想象成一个大的顶级函数。
怎么保证能弹出0,1, 2呢。
解决之道:以毒攻毒!
再创建个闭包!!
var fn = function() { var divs = document.querySelectorAll('div'); for (var i = 0; i < 3; i++) { divs[i].onclick = (function(i) { return function() { alert(i); }; })(i); } }; fn();
或者如下的写法:
var fn = function() { var divs = document.querySelectorAll('div'); for (var i = 0; i < 3; i++) { (function(i) { divs[i].onclick = function() { alert(i); }; })(i); } }; fn();
因此原题如果也想setTimeout也弹出0,1,2的话,改成如下:
for (var i = 0; i < 3; i++) { setTimeout((function(i) { return function() { console.log(i); }; })(i), 0); console.log(i); }
本文完。
相关推荐
JavaScript 面试题知识点总结 JavaScript 面试题是前端开发领域中常见的面试题,涵盖了 JavaScript 基础、Web 前端开发等方面的知识点。本文将对 JavaScript 面试题的知识点进行总结,包括 Object.create、...
面试题集合通常包含各种问题,旨在考察候选人在JS基础、jQuery库以及Ajax技术方面的理解和应用能力。现在,让我们深入探讨这些关键知识点。 1. **JavaScript基础**: - 变量与数据类型:了解`var`, `let`, `const`...
### JavaScript面试题解析 #### 一、ES6的新语法 ES6(ECMAScript 6)是JavaScript语言标准的一个重要版本,它引入了许多新的特性和语法改进,旨在提高开发效率和代码可读性。以下是一些重要的新特性: 1. **let ...
JavaScript面试题汇总涵盖了广泛的知识点,以下是其中一些关键点的详细说明: 1. **变量声明**:在JavaScript中,可以使用var关键字声明变量。在给定的题目中,选项A `( )` 是一个无效的声明,因为没有提供任何值,...
根据给定的文件信息,以下是对每一道JS面试题的知识点进行详细解析: ### 第一题:编写一个方法求一个字符串的字节长度 #### 解析: 在这道题目中,我们需要编写一个函数来计算字符串的字节长度。这里的重点在于...
为了提高 JavaScript 的性能,需要遵循严格模式、将 js 脚本放在页面底部、将 js 脚本将脚本成组打包、使用非阻塞方式下载 js 脚本、尽量使用局部变量来保存全局变量、尽量减少使用闭包、使用 window 对象属性方法时...
这份“JavaScript面试题”文档旨在帮助你深入理解JavaScript的基础和高级概念,以便在面试中展现出扎实的技术功底。以下是一些关键的知识点,涵盖了标题和描述中提及的JavaScript面试重点。 1. **基本语法与数据...
"JavaScript面试题集锦详解" 这篇文章涵盖了JavaScript的多个方面,包括eval函数、window和document对象、null和undefined的区别、数组的map方法、事件机制、use strict模式等。 eval函数 eval函数可以将字符串...
JavaScript是一种广泛应用于Web开发的...以上是对这套JavaScript面试题的解析,涵盖的知识点全面,适合准备JavaScript面试的开发者进行自我检测和学习。理解并掌握这些知识点,对于提升JavaScript编程能力大有裨益。
3. **JS面试题**:JavaScript是前端开发的基础,面试中会涵盖ES6+的新特性(如箭头函数、类、Promise、async/await等),原型链,闭包,作用域,异步编程,数据结构与算法等。 4. **HTTP面试题**:理解HTTP协议对于...
Vue.js面试题大全 Vue.js是一款流行的前端框架,具有强大的数据绑定能力和灵活的组件化开发模式。以下是Vue.js面试题大全的摘要信息: 1. Vue的基本原理 当一个Vue实例创建时,Vue会遍历data中的属性,并使用...
### Vue.js 面试题知识点详解 #### 一、MVVM 概念与 Vue.js 架构 **1. 对于 MVVM 的理解** MVVM(Model-View-ViewModel)是一种软件架构设计模式,主要应用于简化用户界面的开发。在 Vue.js 中,这种模式得到了...
3. **JS面试题**: JavaScript是前端开发的基础,面试中会涉及到语言特性、数据类型、作用域、闭包、原型链、异步编程(回调、Promise、async/await)、事件循环、错误处理等。 4. **HTTP面试题**: 理解HTTP协议...
"React.js 面试题整理" React.js 是一个流行的前端框架,它提供了一种declarative编程模型,可以帮助开发者构建快速、可维护的用户界面。以下是React.js相关知识点的总结: 1. React 事件机制 React并不是将click...
Vue.js 面试题整理带答案 Vue.js 是一个基于MVVM 模式的前端框架,它使用数据劫持结合发布者-订阅者模式来实现双向数据绑定。下面是 Vue.js 相关知识点的总结: 1. Vue 基础 * Vue 实例创建时,Vue 会遍历 data ...
以下是一些基于给定面试题集锦的知识点详细说明: 1. **undefined 和 null 的区别**:`undefined` 表示变量已声明但未定义,而 `null` 是一个特殊值,表示一个空或无值的引用。 2. **&& 运算符**:它执行逻辑与...
Java面试题、设计模式面试题、Spring面试题、MyBatis面试题、Memcached面试题、MongoDB面试题、ZooKeepe面试题、RabbitMQ面试题、HTML面试题、CSS面试题、Vue面试题、React面试题、JavaScript面试题、2021java面试题...
根据给定的信息,我们将深入分析并提取出与JavaScript面试题相关的知识点。 ### 一、单选题 #### 1. 以下哪条语句会产生运行错误: - **选项A**: `var obj = ();` - **解析**:在JavaScript中,括号`()`通常用于...