之前对于闭包的理解只是很肤浅的,只是浮于表面,这次深究了一下闭包,下面是我对闭包的理解。

什么是闭包?

引用高程里的话 =>

闭包就是有权访问另一个作用域中变量的函数,闭包是由函数以及创建该函数的词法环境组成而成

记住,闭包是一个函数,废话不多说,先来个例子:

function makeFunc() {
var name = "Mozilla";
function displayName() {
alert(name);
}
return displayName;
} var myFunc = makeFunc();
myFunc();

函数makeFunc里面定义了一个函数,该函数引用了一个makeFunc内部的变量,然后返回该函数,myFunc变量承接了makeFunc返回的函数,也就是

function displayName() { alert(name); }

在下面执行该函数,咦!!!为什么函数里面的name是从哪里得到的,为什么不报错呢?

要搞明白其中的细节,必须从理解函数被调用的时候都会发生什么入手

那么什么是作用域链呢?

当一个函数被调用的时候,会创建一个执行环境(执行上下文),以及相应的作用域,在函数执行完的时候该作用域链会被销毁,函数里面的变量也会被回收,但是闭包的情况不是这样的..



还是上面的例子,打印了一下myFunc函数,发现该函数的作用域链(scopes)保存着name变量,name变量并没有被回收,于是我们成功的在makeFunc函数外的作用域取到了name变量,myFunc就是闭包,我们重温一下闭包的特点=>闭包是由函数以及创建该函数的词法环境组成而成.

这下概念就理解的更清楚了吧!

闭包与变量

<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<title>Document</title>
</head> <body>
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
<script type="text/javascript">
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
var helpText = [
{ 'id': 'email', 'help': 'Your e-mail address' },
{ 'id': 'name', 'help': 'Your full name' },
{ 'id': 'age', 'help': 'Your age (you must be over 16)' }
]; function returnHelp(i) {
return helpText[i];
} function setupHelp() {
for (var i = 0; i < helpText.length; i++) {
var item = returnHelp(i);
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
</script>
</body> </html>

本来想实现一个点击输入框,显示提示信息的功能,但是结果是显示的全是age信息。。。为什么???

原因是赋值给onfouse的是闭包,这些闭包是由他们的函数定义和在setupHelp作用域中捕获的环境所组成的 这三个闭包在循环中被创建,但他们共享同一个词法作用域,在这个作用域中存在一个变量item, 点击输入框的时候,item已经变成helpText 的最后一个了,所以。。。

怎么解决呢?

既然聚焦输入框后的回调函数里面的item变量引用的是共享的,所以只要把他变成非共享的就可以了,

 function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
var helpText = [
{ 'id': 'email', 'help': 'Your e-mail address' },
{ 'id': 'name', 'help': 'Your full name' },
{ 'id': 'age', 'help': 'Your age (you must be over 16)' }
]; function returnHelp(i) {
return helpText[i];
} function setupHelp() {
for (var i = 0; i < helpText.length; i++) {
var item = returnHelp(i);
//通过这种方法把每一个item变成具体的实例,而不是变量,避免每一次聚焦的时候执行函数里面的变量都是指向同一个数
document.getElementById(item.id).onfocus = change(item);
}
} function change(item) {
return function() {
showHelp(item.help);
}
}
setupHelp();

这样把每一次循环的item传给外面的函数,然后外边函数返回具体的实例而非变量,这样就解决了问题。

下面再来看一个经典的例子 :

<html>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<script>
var nodes = document.getElementsByTagName('div');
for(var i = 0, len = nodes.length; i < len; i++){
nodes[i].onclick = function(){
alert(i);
}
};
</script>
</body>
</html

如果你认为点击每一个div会返回对应的索引,那么恭喜你,你掉坑了!!!

没错,一开始我也掉坑了,可能大家会感到很疑惑,为什么点完全是5,因为点击事件是异步的,点击的时候变量 i 已经变成5了,所以点击的时候弹出的全是5,怎么解决呢,闭包来了

for(var i = 0, len = nodes.length; i < len; i++){
(function(i){
nodes[i].onclick = function(){
console.log(i);
}
})(i)
};

这段代码是如何完美的解决问题的呢?

当我们点击的时候,回调函数里面的 i 引用的是外面闭包的 i ,这样问题就被完美的解决了。

闭包的缺点

在上面分析的过程中也说到过,闭包不会被垃圾回收机制回收,会造成内存泄漏,记得闭包使用完手动把变量回收一下。

【js基础修炼之路】— 深入浅出理解闭包的更多相关文章

  1. 【js基础修炼之路】— 我理解的原型链

    提起原型链,大家并不陌生,但是对于新人来说一提到原型方面的东西就会比较懵.在我自一次面试的时候,面试官也给我提了这样的问题,当时就按照我的理解说了一些,但是很肤浅,在此我希望给刚入门的前端小伙伴聊一下 ...

  2. 【js基础修炼之路】- 手把手教你实现bind

    手写bind前我们先回顾一下bind有哪些特性,以便更好的理解bind和实现bind. bind的特性 var obj = { a: 100, say(one, two) { console.log( ...

  3. 【js基础修炼之路】- 微任务,宏任务和Event-Loop

    一段代码让你了解Event-Loop console.log(1); setTimeout(() => { console.log(2); }, 0); new Promise((resolve ...

  4. 【js基础修炼之路】— null和undefined的区别

    在近期的复习期间遇到null和nudefined,于是通过查找资料,想写一篇文章来说明他们的区别.. javaScript高级程序设计: 在使用var声明变量但未对其加以初始化时,这个变量的值就是un ...

  5. 【js基础修炼之路】--创建文档碎片document.createDocumentFragment()

          讲这个方法之前,我们应该先了解下插入节点时浏览器会做什么.         在浏览器中,我们一旦把节点添加到document.body(或者其他节点)中,页面就会更新并反映出这个变化,对于 ...

  6. Js基础知识6-JavaScript匿名函数和闭包

    匿名函数 1,把匿名函数赋值给变量 var test = function() { return 'guoyu'; }; alert(test);//test是个函数 alert(test()); 2 ...

  7. 【css基础修炼之路】— 谈谈元素的垂直水平居中

    作为一个初级的前端工程师,在开发的过程中遇到了许多问题,其中使元素垂直居中这个问题难住了我,可能在大家看来这是一个非常小的问题,但是却困扰了我很长时间,于是决定做一个总结!!! 废话不多说,直接上代码 ...

  8. javaScript深入浅出之理解闭包

    javaScript深入浅出之理解闭包 引言 闭包是个老生长谈的话题了,对于闭包网上也有很多不同的看法 <你不知道的javaScript>对于闭包是这么定义的:函数创建和函数执行不在同一个 ...

  9. [js高手之路]深入浅出webpack教程系列6-插件使用之html-webpack-plugin配置(下)

    上文我们对html-webpack-plugin的实例htmlWebpackPlugin进行了遍历分析,讲解了几个常用属性( inject, minify )以及自定义属性的添加,本文,我们继续深入他 ...

随机推荐

  1. js-eval运算符

    js中使用eval运算符需要注意—— eval()只有一个参数 传入的参数是字符串时,才会去解析执行:否则,将直接返回这个参数 作用域与调用它的变量作用域保持一致 返回字符串中最后一个表达式或语句的值 ...

  2. 读经典——《CLR via C#》(Jeffrey Richter著) 笔记_值类型的装箱和拆箱(二)

    [注意]:如果知道自己写的代码会造成编译器反复对一个值类型进行装箱,请改成用手动方式对值类型进行装箱. [好处]:代码会变得更小.更快. [例子]: using System; public seal ...

  3. Flask&&人工智能AI --5 Flask-session、WTForms、数据库连接池、Websocket

    未完待续.... DButils 什么是数据库连接池 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库 ...

  4. zabbix 安装使用

    zabbix是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案. zabbix能监视各种网络参数,保证服务器系统的安全运营:并提供灵活的通知机制以让系统管理员快速定位/解决 ...

  5. classloader 学习

    classloader就是把类文件加载到jvm中供虚拟机使用,先看一个magic小例子: 首先,我定义一个alex/vicky包,然后在这个包内定义一个接口: public interfaceISer ...

  6. Car Flash ECU Programmer From autonumen

    Whether you’re a home car owner or an auto mechanic — you can save thousands of dollars on car maint ...

  7. 2.4 Rust Ownership

    What Is Ownership ownership这个单词有些不好翻译,刚开始就直接叫它“ownership”即可.这里简单说一下,我对它的理解, 从“数据结构与算法”的角度来看,ownershi ...

  8. 异地clone RAC数据库 +ASM USE RMAN

    ###sample 如何在本地生成数据库的备份,并复制到DG库新环境(高级) 1. 首先确定本地文件系统(存放备份集)足够大,可以使用如下语句查询当前数据库实际的使用总大小 Rman 备份进度: se ...

  9. Troubleshooting ORA-201 and ORA-202 Error

    ---- 3. When lowering the value of COMPATIBLE: You cannot start the database with lower compatibilit ...

  10. Python字符和编码

    1. 字符和编码 背景 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特(bit)作为一个字节(byte). 由于计算机是美国人发明的,因此, ...