网站首页 > 技术文章 正文
《javascript高级程序设计》中闭包的概念:
闭包,其实是一种语言特性,它是指的是程序设计语言中,允许将函数看作对象,然后能像在对象中的操作般在函数中定义实例(局部)变量,而这些变量能在函数中保存到函数的实例对象销毁为止,其它代码块能通过某种方式获取这些实例(局部)变量的值并进行应用扩展。
我们的理解:
其实闭包就是一个函数,一个外部函数通过调用函数并return返回出内部函数,这里的内部函数就是一个闭包;此时在内部函数中是可以访问到外部函数的变量的;
要想理解闭包,首先我们要了解栈堆内存和作用域链;首先我们来讲解栈堆内存:
首先我们来看个demo:
var a=1;
var obj={"name":"咸鱼"}
上面简单的两句代码,其实就是在内存中做了两件事,效果图如下:
image.png
在js简单实现深浅拷贝(https://www.cnblogs.com/dengyao-blogs/p/11466598.html)一文中我们知道基本数据类型是存储在栈内存中的,引用数据类型是存储在堆内存中的,其实上面的两句代码在内存中就是做了两件事:1.首先在栈内存中开辟了一块空间用来存放a的变量和值;2.在堆内存中开辟了一块空间用来存储obj的值,同时在将地址指向栈内存中的变量名obj
我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。
如果我们在代码下面再加上一句obj={"name":'张三"},这个时候我们之前存储name为咸鱼的值也就是obj原来的值会被js中的垃圾回收机制回收掉,然后obj的值重新的指向{name:"张三"}这个值;
作用域链
再来看一下这个例子:
var a = 1;
function fn(){
var b = 2;
function fn1(){
console.log(b);//2
console.log(a);//1
}
fn1();
}
fn();
效果图如下:
image.png
1.var a=1;这个时候我们是在全局执行环境的,浏览器的全局环境就是window作用域,我们的window作用域中有a和fn;
2.当我们往下走到fn的时候,栈内存会开辟一块新的执行环境,此时fn的执行环境中我们有b和fn1;
3.当我们接着往下走到fn1的时候,这时栈内存同样会开辟一块新的执行环境,此时fn1的执行环境中是没有任何变量数据的,但是我们在fn1中输出a、b,我们都是可以读取到的;这是因为程序在读取变量的时候是从内到外的开始读的,是随着fn1开始往上一层一层的查找,是这样的执行顺序(fn1 = > fn = > window),如果找到window中还没有读取到变量,这时程序才会报错;
当然在执行的过程中,垃圾回收机制如果检测到程序执行完了是会进行垃圾回收的,避免造成内存泄露等问题;就是说我们的fn1里面执行完之后fn1的作用域就会被销毁,接着程序执行fn,fn执行完之后fn就会被销毁;往上执行到全局的时候,整个程序就没有了fn的作用域和fn1的作用域,只剩下浏览器的全局作用域window,这个时候window里只剩a和fn;
了解了上面的作用域链和栈内存和堆内存的知识之后,我们来开始讲解js闭包:
function outer() {
var a = '123'
return function add(){
//在这里因为作用域的关系,add是能访问到outer的所有变量的,但是outer是访问不到add的变量;
//所以思路一转,把add的值作为结果return出来变通实现outer外部函数访问到了内部函数变量
// add就是一个闭包函数,因为他能够访问到outer函数的作用域,add中没有找到变量a,则会继续往上层作用域找
console.log(a);
}
}
var inner = outer() // 获得add闭包函数
inner() //"123"
首先我们可以看到,在全局作用域下我们是有一个outer函数的,outer作用域里面有a和add,add作用域里面执行控制台输出a的变量,此时这里的add函数就形成了一个闭包,因为add函数里面需要访问到outer作用域下的a变量,而他们不处在同一个作用域中,所以两者相互牵引,需要输出a,上面outer中的变量a就必须得在,作用域链查找到outer的时候找到a了,输出a的时候,垃圾回收机制会认为add还没有执行完成,因为此时的作用域链查找已经到了outer作用域下,所以不会清理a的内存空间;所以这就会带来一个问题:如果我们多次的使用闭包,则会给我们的程序带来内存占用过多,导致性能问题;
函数内部能访问全局变量是javascript语言的特殊之处,但是如果我们想达到函数外部能访问内部变量的时候,我们就可以使用闭包,这就是闭包给我们带来的便利;
闭包的优缺点:
优点:
1.可以读取函数内部的变量;
2.可以避免全局污染
缺点:
1.闭包会导致变量不会被垃圾回收机制所清除,会大量消耗内存;
2.不恰当的使用闭包可能会造成内存泄漏的问题;
总结:
1.作用域链查找变量的方式是一层一层的往上查找,直到找到为止,如果找到window全局作用域还未找到,就报undefined;
2.嵌套函数中,因为不在同一作用域,正常情况下内外部函数是访问不到内部函数的,但是通过闭包可以实现;
3.尽可能少的使用闭包,因为会造成内存消耗大以及有可能造成内存泄露(如果不需要的时候,不要随便使用);
作者:有梦想的咸鱼前端
链接:https://www.jianshu.com/p/a9fa4b58c777
猜你喜欢
- 2025-06-23 JavaScript 开发者常犯的 10 个典型错误
- 2025-06-23 这7道问题可以检验你掌握Javascript的真正实力
- 2025-06-23 HarmonyOS NEXT 如何有效地进行内存管理和避免内存泄露?
- 2025-06-23 JS 函数柯里化(js函数柯里化面试题)
- 2025-06-23 《深入理解javascript原型和闭包系列》 知识点整理
- 2025-06-23 JS面试题突破:什么是闭包?(闭包 js)
- 2025-06-23 JavaScript闭包(javascript 闭包)
- 2025-06-23 “闭包到底是什么?90% 的前端开发者都没真正搞懂!”
- 2024-10-04 两个小案例让你深入理解 Javascript 闭包和作用域
- 2024-10-04 js中的闭包 js闭包的使用
你 发表评论:
欢迎- 560℃Oracle分析函数之Lag和Lead()使用
- 556℃几个Oracle空值处理函数 oracle处理null值的函数
- 546℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 542℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 539℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 531℃【数据统计分析】详解Oracle分组函数之CUBE
- 519℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 509℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端富文本编辑器 (47)
- 前端路由 (61)
- 前端数组 (73)
- 前端排序 (47)
- 前端密码加密 (47)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
- 前端登录页面 (48)
本文暂时没有评论,来添加一个吧(●'◡'●)