个人对js闭包的理解
闭包算是前端面试的基础题,但我看了很多关于闭包的文章博客,但感觉很多对于闭包的理想还是有分歧的,现在网上对闭包的理解一般是两种:
- 有些文章认为闭包必须要返回嵌套函数中里面用到外面函数局部变量的方法才叫闭包,有两个条件:1)、函数嵌套,内部函数要用到外部函数的局部变量 2)、内部函数必须返回
- 有些文章认为只要函数嵌套内部函数用到了外部局部变量就是闭包,不要返回内部函数
我们先看看闭包的定义到底是什么,然后在来分析我在学习js的时候不同阶段对闭包的误解。在《javascript高级程序设计中》对闭包定义是这样的:“闭包是指有权限访问另一个函数作用域中的变量的函数。“这里没有提到这个函数必须要return出来,我们在看看《javascript语言精粹》中对闭包的定义是用一段很误导人的代码例子来解释闭包:
var quo=function(status){
return{
get_status:function(){
return status;
}
}
} var myQuo=quo("amazed");
document.writeln(myQuo.get_status());
"即使quo返回了,但get_status方法仍然享有访问quo对象的status属性的特权,get_status方法并不是访问该参数的一个副本,它访问的是该参数本身,只是可能的,因为该函数可以访问它被创建时所处的上下文环境,这被称为闭包. "
这是很多解释闭包的文章最常用的解释案例,所以导致新手第一次看这种解释产生一个误导,说必须要return这个函数,但我们看看《javascript语言精粹》这段解释中最后强调的是该函数可以访问被创建时所处的上下文环境,强调的是访问外部函数的局部变量,
而用这个例子是因为这个例子是闭包的更为复杂的应用,因为你在函数嵌套中,内部函数的运行只能在外部函数中执行,要在全局变量中运行不了,如果我们要在全局运行一个比较容易理解的方法是:
var get_status;
var quo=function(status){
get_status=function(){
return status;
}
} quo("amazed"); document.writeln( get_status());
那这种是不是闭包呢?对上面代码进行优化利用js 可以return函数代码简化了很多。所以在我的理解只要调用了外部函数变量的函数都是闭包,而之所以对闭包的介绍都用那个案例是因为那个算是闭包的经典复杂的应用所以基本介绍闭包的都会介绍那个案例,这样反而误导了刚学习闭包的同学必须要return出去,下面我在说说我理解闭包踩过的坑。
刚开始我对闭包理解就是匿名自执行函数,匿名自执行函数如下代码:
(function(){})()
//or
var dem=(function(){}())
这个匿名自执行用到最多的当然是jQuery里面:
(function ($) { })(jQuery)
在jQuery插件中经常会看到这种写法,这样写的目的是为了$变量有可能会在网页中被定义成其他值,而为了避免$符号的冲突而将jQuery这个对象赋值成局部$变量。这样就不会影响全局$。
还有一种用法是:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6]();
因为js没有块作用域所以导致i其实是全局变量,所以a中的所以方法里面的i都是10,解决这个问题可以用es6 中let,但目前有些浏览器不支持es6,谷歌已经支持let关键字了,现在做法是用es6的写法通过node插件转换成es5的写法,还用一种解决方法是:
var a = [];
for (var i = 0; i < 10; i++) {
(function(i){
a[i] = function () {
console.log(i);
};
})(i)
}
a[6]();
用匿名自执行将i赋值到成局部变量i所以a数值中的方法是调用了匿名方法中的局部变量i,这里也用到了闭包,所以当时我误解为闭包其实就是将全局变量转换为局部变量。面试的时候我也是这么回答,面试官一脸懵逼啊。。。。。
第二阶段的误解就是必须要return的那个函数,所以对于上面的匿名执行方法我斩钉截铁的说那个不是闭包啊!面试的时候面试官又一脸懵逼。。。。。
看了《javascript高级程序设计》和《javascript语言精粹》才慢慢对闭包有了全面的了解。这是我对闭包的理解可能也会有理解不到位的地方,欢迎留言指正,本来成长的过程就是对某件事物的理解从片面到慢慢全面的过程,而成长就要大家一起讨论辩论大家阐述自己的观点才能认识的更全面。下面在几个对闭包的复杂应用的案例。
闭包在实际项目中应用说白了就是一个函数中要频繁操作一些变量,但这个变量在函数中定义在函数每次运行的时候都要重新声明分配内存空间一来麻烦二来感觉消耗内存(用过C#和java的都应该很熟悉垃圾处理机制,js的垃圾处理机制类似不多详细介绍了)但设置成全局变量又会污染全局变量,在js性能优化中提到尽量不要污染全局变量,自然外面套成函数的方式就出来了也就是闭包咯,但又要保证内部函数的运行灵活性不能限制在外层闭包的作用域,return这个函数的方式就出来了,外层函数不想用变量存储而且还要单独运行一次,那么匿名自执行的的方式出来了,ok需求演变导致最后的组合书写方式。在这里在讲讲闭包的缺点,闭包一个缺点是让调用的变量会一直存储在内存得不到释放,这个缺点在实际项目需求中有不同的解决方法:
第一场景:模块
var serial_maker=function () {
var prefix='';
var seq=0;
return{
set_prefix:function (p) {
prefix=new String(p)
},
set_seq:function (s) {
seq=s;
},
gensym:function () {
var result=prefix+seq;
seq+=1;
return result;
}
}
}
var seqer=serial_maker();
seqer,set_prefix('Q');
seqer.set_seq(1000);
var unique=seqer.gensym(); //unique是"Q1000"
这是也很常见的闭包,用意就是不想把prefix和seq声明为全局变量,而污染全局变量,在变量生命周期的角度其实和全局变量是一样的,prefix和seq会一直存在内存中直到页面关闭,javascript高级程序设计中说变量会在函数执行完毕后就进行释放,而全局变量会一直在内存中直到浏览器关闭,书还提到在浏览器关闭的时候要将用到的dom对象的变量赋值null,因为浏览器貌似不能释放dom对象,这点我有点模糊记不太清楚了,我复习javascript高级程序设计的时候看到了在单独写一遍随便。解决闭包的这个缺点只需在外面加一层匿名自执行函数就可以了
(function () {
var serial_maker=function () {
var prefix='';
var seq=0;
return{
set_prefix:function (p) {
prefix=new String(p)
},
set_seq:function (s) {
seq=s;
},
gensym:function () {
var result=prefix+seq;
seq+=1;
return result;
}
}
}
})();
闭包中用到的变量的生声明周期其实是跟着调用这个函数的上一函数的生命周期的,这个例子的中的prefix和seq是在匿名函数执行后就被释放了。这也是在平时写js时候尽量在外面套一层匿名自执行一来不会污染全局变量,二来在匿名函数执行完了里面的的变量就释放掉了,相对在性能优化上做了一点点贡献和优化吧!
第二场景:同样在事件绑定上也可以用匿名闭包,性能确定就存在了,你在设置全局变量和闭包其实一个内存占用量。不可避免的。
第三场景:柯里化
柯里化就是将函数的参数传递给另一个函数操作,一般用到柯里化是在调用ajax成功之后将数据绑到到页面上时候用到,但随着promise的出现,其实柯里化用的很少了。下面是一个更为复杂的应用,让我佩服的五体投地:
Function.prototype.curry=function () {
var slice=Array.prototype.slice,
args=slice.apply(arguments),//因为arguments不是真正的数组,只是类似数组的一个对象,所以这里要将arguments转换为数组
that=this;
debugger;
return function () {
return that.apply(null,args.concat(slice.apply(arguments)))
}
};
var add=function () {
var total=0;
for(var i=0;i<arguments.length;i++){
total+=arguments[i];
}
return total;
};
var add1=add.curry(1);
document.writeln(add1(6));//
以上场景都是参照《javascript语言精粹》。
转发
个人对js闭包的理解的更多相关文章
- JS闭包的理解及常见应用场景
JS闭包的理解及常见应用场景 一.总结 一句话总结: 闭包是指有权访问另一个函数作用域中的变量的函数 1.如何从外部读取函数内部的变量,为什么? 闭包:f2可以读取f1中的变量,只要把f2作为返回值, ...
- 【闭包】JS闭包深入理解
先看题目代码: 1 2 3 4 5 6 7 8 9 10 11 12 function fun(n,o) { console.log(o) return { fun:function(m){ ...
- 浅谈对Js闭包的理解
理解Js的闭包,首先让我们先看几个概念 执行环境(executive environment)每个函数都有自己的执行环境,匿名函数默认为全局环境. 作用域链(scope chain)子函数继承父函数, ...
- js 闭包原理理解
问题?什么是js(JavaScript)的闭包原理,有什么作用? 一.定义 官方解释:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分. 很显然 ...
- 从循环添加事件谈起对JS闭包的理解
1.引子 相信很多初学js的人,都遇到这样一种情况:想要给一堆按钮添加各自的事件,比如点击第i个按钮时,弹出i这个值.理所当然地,我们会这样写: var buttons = document.getE ...
- js闭包的理解-目前网上分析的最透彻文章
js的闭包对于大家实际上并不陌生,但是真正敢说自己完全理解的人并不多.笔者在网上看到分析闭包的文章非常多,篇幅用的非常多,但是实际上分析的并不到位,或者根本就是不正确的.我有时候都在想,写这些文章的人 ...
- 对JS闭包的理解
闭包,是JS里很重要的一个概念,也是相对来讲不太容易理解的一个东西,不过即使难理解,我们也要迎难而上啊,嘿嘿,网上有很多文章都在讲闭包,我在看JS设计模式的时候,书里也着重讲了闭包,但是书里官方的的确 ...
- JS闭包的理解
闭包的两个特点: 1.作为一个函数变量的一个引用 - 当函数返回时,其处于激活状态.2.一个闭包就是当一个函数返回时,一个没有释放资源的栈区. 其实上面两点可以合成一点,就是闭包函数返回时,该函数内部 ...
- JS闭包深入理解(理解篇)
看书的时候很是不明白为啥变量老是五,经过认真思考的出一下理解: function box() { var arr = []; for (var i = 0; i < 5; i++) { ...
随机推荐
- android复习第二天------布局
1,在4,0版本前一共有五种布局,且都是ViewGroup的子类分别是 RelativeLayout(相对),AbsoluteLayout(绝对),LinearLayout(线性),FrameLayo ...
- fackbook的Fresco (FaceBook推出的Android图片加载库-Fresco)
[Android开发经验]FaceBook推出的Android图片加载库-Fresco 欢迎关注ndroid-tech-frontier开源项目,定期翻译国外Android优质的技术.开源库.软件 ...
- kafka迁移数据目录
问题 先前存储kafka日志的磁盘空间太小,zabbix警报不断,于是加了磁盘,将日志存到新磁盘上. 解决方案 依次在每台机器上操作,保证有机器能响应producer和consumer的操作. 加磁盘 ...
- R语言书籍的学习路线图
现在对R感兴趣的人越来越多,很多人都想快速的掌握R语言,然而,由于目前大部分高校都没有开设R语言课程,这就导致很多人不知道如何着手学习R语言. 对于初学R语言的人,最常见的方式是:遇到不会的地方,就跑 ...
- PAT乙级 1003. 我要通过!(20)
答案正确”是自动判题系统给出的最令人欢喜的回复.本题属于PAT的“答案正确”大派送 —— 只要读入的字符串满足下列条件,系统就输出“答案正确”,否则输出“答案错误”. 得到“答案正确”的条件是: 1. ...
- 最长上升子序列O(nlogn)算法详解
最长上升子序列 时间限制: 10 Sec 内存限制:128 MB 题目描述 给定一个序列,初始为空.现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置.我们想知道此时最长上升子 ...
- Android自动化压力测试快速入门教程(图解)——MonkeyRunner
一.MonkeyRunner测试环境配置(转自) 1. android-sdk 下载地址:http://www.android-doc.com/sdk/index.html 下载完成后,只需要解压就 ...
- scala匿名函数
package com.ming.test import scala.math._ object AnonymousFunc { def valueAtOneQuarter(f:(Double)=&g ...
- Linux终端乱码的解决办法
用SSH连接Linux时经常会遇到乱码的情况,痛苦了好久,在网上找到一个解决办法,编辑~/.bash_profile文件,加入下面两行: LANG="zh_CN.GB18030" ...
- iOS 序列化与反序列化
开篇 1到底这个序列化有啥作用? 面向对象的程序在运行的时候会创建一个复杂的对象图,经常要以二进制的方法序列化这个对象图,这个过程叫做Archiving. 二进制流可以通过网络或写入文件中(来源于某教 ...