一道JS前端闭包面试题解析


Posted in Javascript onDecember 25, 2015

问题

代码A

function fun(n,o){
  console.log(o);
  return {
    fun:function(m){//[2]
      return fun(m,n);//[1]
    }
  }
}

var a=fun(0);
a.fun(1);
a.fun(2);
a.fun(3);
var b=fun(0).fun(1).fun(2).fun(3);
var c=fun(0).fun(1);
c.fun(2);
c.fun(3);

求出程序输出

这是一个闭包测试题

转换为等价代码

return返回的对象的fun属性对应一个新建的函数对象,这个函数对象将形成一个闭包作用域,使其能够访问外层函数的变量n及外层函数fun,为了不将fun函数和fun属性搞混,我们将上述代码修改如下:
代码B

function _fun_(n,o){
  console.log(o);
  return {
    fun:function(m){
      return _fun_(m,n);
    }
  }
}

var a=_fun_(0);//undefined
a.fun(1);//0
a.fun(2);//0
a.fun(3);//0

var b=_fun_(0).fun(1).fun(2).fun(3);
//undefined,0,1,2

var c=fun(0).fun(1);//undefined,0,
c.fun(2);//1
c.fun(3); //1

那么就有同学问了,为什么可以这样改呢,你怎么能确定[1]处的fun不是[2]代码所在处的fun呢,要知道此处的fun属性可是指向一个函数对象哦~
这里就要说到JS的词法作用域,JS变量作用域存在于函数体中即函数体,并且变量的作用域是在函数定义声明的时候就是确定的,而非在函数运行时。
如下代码

var name="global";
function foo(){
  console.log(name);
}

function fooOuter1(){
  var name="local";
  foo();
}
fooOuter1();//输出global 而不是local,并且和闭包没有任何关系

function fooOuter2(){
  var name="local";
  function foo(){
    console.log(name);
  }
  foo();
}
fooOuter2();//输出local 而不是global,在函数声明是name变量作用域就在其外层函数中,嗯嗯就是闭包~

好了我们回到题目,在函数声明定义阶段,[2]处的匿名函数进行定义声明,发现在[1]处需要引用一个名为fun的函数对象,那么首先在当前函数体内寻找,发现没有,那么就到其外层函数-这个匿名函数的包裹函数中去查找,发现也没有,到外层函数中去,发现外面没有函数包裹了,那就到全局环境下去找,额偶终于找到了......就把fun函数指定为全局环境下的fun函数对象并加入到匿名函数的闭包中去。至此我们就知道代码B为什么和代码A是等价的了~~~

创建闭包作用域

JS在词法分析结束后,确定了1个闭包,就是返回的对象fun属性对应的匿名函数的闭包-访问全局环境下的_func_及其外层函数的函数内部变量n;
在每次_func_执行的时候,都会将闭包中变量的作用域信息传递到函数执行环境中,供函数执行时获取变量值时使用

执行输出

var a=_fun_(0);//undefined
a.fun(1);//0
a.fun(2);//0
a.fun(3);//0

_fun_函数执行,因为第2个参数未定义,输出undefined。然后返回一个对象,带有fun属性,指向一个函数对象-带有闭包,能够访问到_fun_和变量n_
a.fun(1)执行返回的对象的fun方法,传入m的值1,调用返回_fun_(1,0)
所以输出为0,a.fun(2),a.fun(3)和a.fun(1)

var b=_fun_(0).fun(1).fun(2).fun(3);
等价代码:

var b=_fun_(0);
var b1=b.fun(1);
var b2=b1.fun(2);//[3]
var b3=b2.fun(3);//[4]
前2句和上面的输出相同undefined,0,当[3]被调用时,b1对象中有一个闭包,引用了_fun_函数及外层函数变量n=1,所以匿名函数执行的函数调用为_fun_(2,1),输出结果为1,并返回一个新的对象。
当[4]执行时,b2对象也有一个闭包,引用了_fun_函数及外层函数变量n=2,执行_fun_(3,2),输出结果为2

var c=fun(0).fun(1);//undefined,0,
c.fun(2);//1
c.fun(3); //1

能看懂前面的代码执行解释,理解上面的代码执行输出就不会有问题了,希望大家喜欢。

Javascript 相关文章推荐
Javascript继承(上)——对象构建介绍
Nov 08 Javascript
YUI Compressor压缩JavaScript原理及微优化
Jan 07 Javascript
JS实现div居中示例
Apr 17 Javascript
《JavaScript DOM 编程艺术》读书笔记之JavaScript 简史
Jan 09 Javascript
jQuery过滤选择器用法分析
Feb 10 Javascript
JavaScript获取文本框内选中文本的方法
Feb 20 Javascript
微信小程序 仿美团分类菜单 swiper分类菜单
Apr 12 Javascript
vue组件如何被其他项目引用
Apr 13 Javascript
三种Webpack打包方式(小结)
Sep 19 Javascript
react 兄弟组件如何调用对方的方法示例
Oct 23 Javascript
微信小程序学习笔记之函数定义、页面渲染图文详解
Mar 28 Javascript
vue swipeCell滑动单元格(仿微信)的实现示例
Sep 14 Javascript
干货分享:让你分分钟学会javascript闭包
Dec 25 #Javascript
javascript生成img标签的3种实现方法(对象、方法、html)
Dec 25 #Javascript
谈谈我对JavaScript中typeof和instanceof的深入理解
Dec 25 #Javascript
JavaScript中Window对象的属性及事件
Dec 25 #Javascript
JavaScript字符串删除重复字符的方法
Dec 25 #Javascript
JavaScript如何实现在文本框(密码框)输入提示语
Dec 25 #Javascript
jquery实现图片预加载
Dec 25 #Javascript
You might like
全国FM电台频率大全 - 12 安徽省
2020/03/11 无线电
关于mysql字符集设置了character_set_client=binary 在gbk情况下会出现表描述是乱码的情况
2013/01/06 PHP
php实现按指定大小等比缩放生成上传图片缩略图的方法
2014/12/15 PHP
php中preg_match的isU代表什么意思
2015/10/01 PHP
php数值转换时间及时间转换数值用法示例
2017/05/18 PHP
Yii2中简单的场景使用介绍
2017/06/02 PHP
Laravel中服务提供者和门面模式的入门介绍
2017/11/06 PHP
Ajax请求PHP后台接口返回信息的实例代码
2018/08/21 PHP
js程序中美元符号$是什么
2008/06/05 Javascript
IE 条件注释详解总结(附实例代码)
2009/08/29 Javascript
extjs 为某个事件设置拦截器
2010/01/15 Javascript
Javascript 命名空间模式
2013/11/01 Javascript
JS、CSS加载中的小问题探讨
2013/11/26 Javascript
js继承call()和apply()方法总结
2014/12/08 Javascript
javascript框架设计之类工厂
2015/06/23 Javascript
js实现三张图(文)片一起切换的banner焦点图
2015/08/25 Javascript
js生成随机数的过程解析
2015/11/24 Javascript
JavaScript 冒泡排序和选择排序的实现代码
2016/09/03 Javascript
基于vue.js实现侧边菜单栏
2017/03/20 Javascript
IScroll那些事_当内容不足时下拉刷新的解决方法
2017/07/18 Javascript
解析JS在获取当前月的最后一天遇到的坑
2019/08/30 Javascript
layui动态加载多表头的实例
2019/09/05 Javascript
Python中文字符串截取问题
2015/06/15 Python
python使用sqlite3时游标使用方法
2018/03/13 Python
详解python读取image
2019/04/03 Python
使用Python opencv实现视频与图片的相互转换
2019/07/08 Python
python多线程实现代码(模拟银行服务操作流程)
2020/01/13 Python
浅谈python print(xx, flush = True) 全网最清晰的解释
2020/02/21 Python
python爬虫调度器用法及实例代码
2020/11/30 Python
css3背景图片透明叠加属性cross-fade简介及用法实例
2013/01/08 HTML / CSS
汽车运用工程毕业生自荐信
2013/10/29 职场文书
幼儿园元旦活动感言
2014/03/02 职场文书
2014年食品安全工作总结
2014/12/04 职场文书
写给纪委的违纪检讨书
2015/05/05 职场文书
Redis Lua脚本实现ip限流示例
2022/07/15 Redis
Go语言编译原理之源码调试
2022/08/05 Golang