由一道面试题简单扩展 — setTimeout、闭包
在一个前端公众号,看到这么一个号称简单的面试题:
1、以下程序输出什么?
<script type="text/javascript">
function init(){
for (var i = 0; i < 10; ++i) {
setTimeout(function () {
console.log(i);
}, 0);
} }
window.onload=init;
</script>
2、若需要输出0123456789,应该怎么修改?
结果,输出的为10101010101010101010
若要输出0123456789,则可以将代码改成
<script type="text/javascript">
function init2(){
for (var i = 0; i < 10; ++i) {
setTimeout(
(function(i){
return function(){
console.log(i)
}
})(i), 0);
}
}
window.onload=init2;
</script>
解释:
1、for循环每次注册一个延迟函数, setTimeout是异步的,传入事件队列中,在循环结束后进行处理,当循环结束时,i为10。
setTimeout中的匿名function没有将 i 作为参数传入来固定这个变量的值, 让其保留下来, 而是直接引用了外部作用域中的 i, 因此 i 变化时, 也影响到了匿名function.
2、for循环执行时,给点击事件绑定的匿名函数传递i后,立即执行返回一个内部的匿名函数,因为参数是按值传递的,
所以此时形参保存的就是当前i的值,内部的匿名函数一直保存着当前i的值。 返回的匿名函数执行弹出各自保存的 i 的引用的值。
看到解释到这里,我是一脸懵逼,关键在于闭包的参数传递,所以我自己对闭包的知识进行了一下下扩展。
0、前言:Js变量的作用域
变量的作用域包括:全局变量、局部变量
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
var n=999;
function f1(){
alert(n);
}
f1(); // 999
在函数外部无法读取函数内部的局部变量
function f1(){
var n=999;
}
alert(n); // error
注意:若函数内部声明变量的时候,一定要用var,否则实际上声明了全局变量,
会造成其他函数误用变量、全局对象过于庞大,影响访问速度等不良影响
function f1(){
n=999;
}
f1();
alert(n); // 999
Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立
1、闭包的解释
闭包是指有权访问另一个函数作用域变量的函数,创建闭包的通常方式,是在一个函数内部创建另一个函数。
闭包的本质是一个函数,将函数内部和外部连接起来的桥梁。
2、闭包的写法
好像,还是,有那么点抽象,那就网上找个例子:
function func1(){
var age = 22;
function func2(){
return age;
}
return func2;
}
var result = func1(); //得到的是函数func2这一整个函数
var _num = result(); //得到的是func2的执行结果也就是age的值
console.log(result); //function func2(){ return age; }
console.log(_num); //22
结果分析:
a)首先要明白一个函数名称加不加括号的区别,比如说func1和func1(),func1表示的是这个函数本身,func1()表示的是这个名叫func1的函数的执行结果也就是它的返回值。
b) 其次我们来分析闭包的原理,因为func2是函数func1的子函数,所以在func2中可以访问到变量age,并将这个值作为函数func2的返回值返回,最后将整个func2函数作为func1函数的返回值。
c)最后我们来分析一下result和_num的值,result是函数func1的执行结果,也就是func1的返回值func2函数本身,_num的值为func2的执行结果也就是变量age。
d)至此,我们实际上已经将局部变量age的值获取到了并存在变量_num中,实现了在函数外部访问局部变量的值的问题。
3、闭包的用途
a) 匿名自执行函数
创建了一个匿名的函数,并立即执行它,由于外部无法引用它内部的变量,因此在执行完后很快就会被释放,关键是这种机制不会污染全局对象。
① 匿名函数:顾名思义,就是没有方法名的函数
② 匿名函数调用方式:
使用()将匿名函数括起来,然后后面再加一对小括号(包含参数列表)
(function(a,b)
{
console.log('匿名函数加圆括号:'+(a+b));
}(,));
将匿名函数赋值给一个变量,通过变量引用进行函数调用
var noName= function(a,b){
console.log('匿名函数赋值变量:'+(a+b));
}(1,4);
b) 缓存(未整理验证)
再来看一个例子,设想我们有一个处理过程很耗时的函数对象,每次调用都会花费很长时间,
那么我们就需要将计算出来的值存储起来,当调用这个函数的时候,首先在缓存中查找,如果找不到,则进行计算,
然后更新缓存并返回值,如果找到了,直接返回查找到的值即可。闭包正是可以做到这一点,因为它不会释放外部的引用,
从而函数内部的值可以得以保留。
c)实现封装
可以先来看一个关于封装的例子,在person之外的地方无法访问其内部的变量,而通过提供闭包的形式来访问
d)面向对象编程
实例独立访问成员变量,互不影响
由一道面试题简单扩展 — setTimeout、闭包的更多相关文章
- 一道面试题与Java位操作 和 BitSet 库的使用
前一段时间在网上看到这样一道面试题: 有个老的手机短信程序,由于当时的手机CPU,内存都很烂.所以这个短信程序只能记住256条短信,多了就删了. 每个短信有个唯一的ID,在0到255之间.当然用户可能 ...
- 关于Java类加载双亲委派机制的思考(附一道面试题)
预定义类加载器和双亲委派机制 JVM预定义的三种类型类加载器: 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面 ...
- 一道面试题比较synchronized和读写锁
一.科普定义 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步关键字相信大家都用得比较多,在上一篇“多个线程之间共享数据的方式”中也详细列举他的应用, ...
- <转>一道面试题比较synchronized和读写锁
一.科普定义(原文:http://903497571.iteye.com/blog/1874752) 这篇博文的两个主角“synchronized”和“读写锁” 1)synchronized 这个同步 ...
- 一道笔试题来理顺Java中的值传递和引用传递
题目如下: private static void change(StringBuffer str11, StringBuffer str12) { str12 = str11; str11 = ...
- 关于一道面试题,使用C#实现字符串反转算法
关于一道面试题,使用C#实现字符串反转算法. 题目见http://student.csdn.net/space.php?do=question&ac=detail&qid=490 详细 ...
- 一道笔试题和UML思想 ~
一句软件工程界的名言,让我想起了一个和一道笔试题有关的故事.希望更多的人了解 UML 背后的思想比他的语法更重要,是笔者写作本文的一点小愿望. 一.从一句软件工程名言说起 对很多事情的处理上,东西方都 ...
- 【死磕JVM】一道面试题引发的“栈帧”!!!
前言 最近小农的朋友--小勇在找工作,开年来金三银四,都想跳一跳,找个踏(gao)实(xin)点的工作,这不小勇也去面试了,不得不说,现在面试,各种底层各种原理,层出不穷,小勇就遇上了这么一道面试题, ...
- 对bootstrap modal的简单扩展封装
对bootstrap modal的简单扩展封装 参考自:http://www.muzilei.com/archives/677 注:原文不支持bootstrap新版本,并且居中等存在问题 此段时间 ...
随机推荐
- ubuntu区域语言(locale)设置(切换为中文)
第一步:编辑文件 代码:sudo gedit /var/lib/locales/supported.d/local (这个文件是所有已经激活的区域语言的列表) 写入相关内容,比如写入如下内容: zh ...
- 微信App支付:微信支付的appid,appsecret,商户号mch_id,微信交易支付密钥(mch_key)在哪里查看
1-1) 查看微信支付 appid 的方法 微信支付使用的 appid, 是微信服务号的 appid, 需要你登录微信服务号后台, 在 开发-基本配置/开发者ID(AppID) 中查看微信支付 app ...
- 一文带您了解5G的价值与应用
一文带您了解5G的价值与应用 5G最有趣的一点是:大多数产品都是先有明确应用场景而后千呼万唤始出来.而5G则不同,即将到来的5G不仅再一次印证了科学技术是第一生产力还给不少用户带来了迷茫——我们为什么 ...
- 采用requests库构建简单的网络爬虫
Date: 2019-06-09 Author: Sun 我们分析格言网 https://www.geyanw.com/, 通过requests网络库和bs4解析库进行爬取此网站内容. 项目操作步 ...
- java 常用API
package com.orcal.demc01; public class Regex { public static void main(String[] args) { // TODO Auto ...
- Nginx 的安装 与 启动
没有图文说明,是我看着视频一步一步照做的,安装过程了,也随便把步骤记录下来了. 我是新装 Linux 服务器,所以安装过程中出现的错误也是第一遇到,希望对你们有所帮助. 也是方便自己以后再次安装 ni ...
- (OpenExplorer For Eclipse)Eclipse 中打开工程目录的插件(转)
我们想在Eclipse中的打开工程目录,Eclipse 自身没有这个功能,我们可以安装一个插件来实现这个功能.具体的操作方法如下: (1).到以下链接中下载插件:https://github.com/ ...
- Hibernate-原生SQL查询
HQL尽管容易使用,但是在一些复杂的数据操作上功能有限.特别是在实现复杂的报表统计与计算,以及多表连接查询上往往无能为力,这时可以使用SQL(Native SQL)实现HQL无法完成的任务. 1.使用 ...
- 使用Service Bus Topic 实现简单的聊天室
创建Service Bus能够參照: https://azure.microsoft.com/en-gb/documentation/articles/service-bus-dotnet-how-t ...
- Android应用之——微信微博第三方sdk登录分享使用过程中的一些常见问题
前言 近期在使用第三方登录和分享的过程中遇到了非常多问题,一方面能够归结为自己经验的不足,还有一方面事实上也说明了官方文档的含糊不清.这篇博文不会写关于怎样使用第三方登录分享,由于官方文档已经写明了步 ...