闭包,在一开始接触JavaScript的时候就听说过。首先明确一点,它理解起来确实不复杂,而且它也非常好用。那我们去理解闭包之前,要有什么基础呢?我个人认为最重要的便是作用域(lexical scope),如果对作用域和作用域链不理解的同学最好自己先去学一学,再回过头来,理解闭包,就更加轻松。

  下面便直接进入主题。

  我们知道一个函数是有作用域的,在函数内部定义的局部变量只有在函数内部才可以访问的到。一旦函数访问结束被销毁,局部变量随之也会销毁,无法通过任何方式再次访问局部变量,除了闭包。也就是说,我们可以通过闭包这一个方法,从函数的外部去访问到函数内部的变量,即使那个函数已经被销毁。没错,闭包最重要的用法就是,我们只提供某些接口去访问和修改局部变量,而外部是不能直接访问到局部变量的。

  说了那么多有关如何使用闭包,我们来看看闭包是长什么样子的。又到了举个栗子的环节,依然是最简单的people和name。

var people = function(){
var name = "Yika";
var sayName = function(){
return name; //访问了people函数的局部变量name
}
var setName = function(newName){
name = newName; //访问了people函数的局部变量name
}
return{
sayName: sayName,
setName: setName
}//返回一个对象
} var p1 = people(); //函数return的是一个对象,这个对象里有两个函数sayName和setName
console.log(p1.name); //undefined. name是people函数里的局部变量,而不是p1对象的属性,当然为undefined
console.log(p1.sayName());//"Yika"
p1.setName("Ruyi"); //通过setName函数修改局部变量name的值
console.log(p1.sayName());//"Ruyi"

  看完这个例子,想必对闭包多少有个了解啦,除了注释的内容,下面再做些补充。

  问:为什么局部变量name属性在people执行完之后,没有被销毁呢,反而数值还保存在内存中。

  答: 在例子中,函数注释那里专门写了(访问了people函数的局部变量name)。正是因为people函数里的sayName函数和setName函数访问了name属性,并且通过return传到了p1对象里,成了p1的两个方法。因为方法一直引用着people函数的局部变量,所以不会被消除,依然会在内存中。这样便形成了闭包,可以在函数外部访问到函数内部的局部变量。

  对此,我们可以换个更直观的写法。

var people = function(){
var name = "Yika"; var obj = {
sayName: function(){
return name;
},
setName: function(newName){
name = newName
}
}; return obj; //直观的返回对象
}
//下面的结果是一样的。

  当然闭包也不是一直那么好用,特别是在循环里。继续举例子

<body>
<input type="button"/>
<input type="button"/>
<input type="button"/>
<input type="button"/>
<input type="button"/>
<input type="button"/>
<input type="button"/>
<script type="text/javascript">
var oBtn = document.getElementsByTagName('input');
for(var i = 0, len = oBtn.length; i < len; i++){
oBtn[i].onclick = function(){
alert("value = " + i); //闭包陷阱的发生地!永远输出 value = 7
}
}
</script>
</body>

  当我们运行上面的代码的时候,我们是这样想的,循环一下按钮,并输出按钮所在的序号,但是我们得到的却永远是7。其实用我们之前讲的闭包会让变量的值一直保存在内存中的原理想一想,就应该懂了。当我们循环的时候按钮的点击事件时,是引用了for循环里的 i 变量。当所有按钮都绑定了点击事件后,i 的值也已经变成了7,当然所有的按钮输出的都是7啦!怎么解决这个问题也很好办的,但是我希望留下给大家思考。这里就说一下大致思路吧,我们可以在循环之外创建一个辅助函数,并让辅助函数return一个绑定当前 i 的函数。

  当然这里我也只是抛砖引玉的介绍了一下闭包,希望可以帮到大家浅显简单的了解闭包。

  还是那句话噢,有问题立即指正,我一定会立马检查更正,以免误导了大家!

JavaScript——以简单的方式理解闭包的更多相关文章

  1. 用最简单的方式理解 IoC 控制反转

    思想引入 假设一个系统原先只设定有一个默认的方法去完成业务,这里举例这个原先设定开发的是 UserDaoImpl(可能有些牵强,但是不影响我们对逻辑的理解)这样一个业务. 后来有一天,需求变了,业务流 ...

  2. 最简单的方式理解Vue的自定义指令与混合

    vue.js 自定义指令 钩子函数:bindinsertedupdatecomponentUpdatedunbind 钩子函数完整实例:html: <div id="hook-argu ...

  3. 最简单的例子理解Javascript闭包

    理解Javascript的闭包非常关键,本篇试图用最简单的例子理解此概念. function greet(sth){ return function(name){ console.log(sth + ...

  4. JavaScript学习总结——我所理解的JavaScript闭包

    一.闭包(Closure) 1.1.什么是闭包? 理解闭包概念: a.闭包是指有权限访问另一个函数作用域的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数,也就是创建一个内部函数,创建一 ...

  5. JavaScript 从闭包可以做什么开始,将有助于理解闭包

    本文内容 函数内部访问全局变量 函数外部不能直接访问局部变量 函数外部访问局部变量 保护私有成员 持久性 模块化 抽象性 闭包是 JavaScript 的重要特性,非常强大,可用于执行复杂的计算,可并 ...

  6. javascript深入理解闭包

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

  7. javascript深入理解闭包(转)

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

  8. 【repost】让你一句话理解闭包(简单易懂)

    接触javascript很久了,每次理解闭包都似是而非,最近在找Web前端的工作,所以需要把基础夯实一下. 本文是参照了joy_lee的博客 闭包 在她这篇博客的基础上以批注的形式力争把我的理解阐述出 ...

  9. javascript花式理解闭包

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

随机推荐

  1. C#ShowCursor光标的显示与隐藏

      使用using System.Runtime.InteropServices; [DllImport("user32.dll" , EntryPoint = "Sho ...

  2. C#通过代码注册COM组件

    using System; using System.Diagnostics; using Microsoft.Win32; namespace ChuckLu.Utility { public cl ...

  3. oracle tns in linux

    [oracle@redhat4 admin]$ cd $ORACLE_HOME/network/admin[oracle@redhat4 admin]$ cat tnsnames.ora# tnsna ...

  4. java-基础练习题

    [程序1] 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 1.程序分析: 兔子的规律为数列1,1 ...

  5. devDependencies和dependencies的区别

    我们在使用npm install 安装模块或插件的时候,有两种命令把他们写入到 package.json 文件里面去,比如: --save-dev --save 在 package.json 文件里面 ...

  6. vpn局域网共享

    vpn局域网共享 更改网关为vpn共享主机地址 在“命令运行符”处,挨个键入下列命令,并回车生效: regsvr32 Softpub.dll regsvr32 Wintrust.dll regsvr3 ...

  7. 1160. Network(最小生成树)

    1160 算是模版了 没什么限制 结束输出就行了 #include <iostream> #include<cstdio> #include<cstring> #i ...

  8. 基于XMPP的即时通信系统的建立(三)— 程序设计概览

    XMPP与HTTP的比较 XMPP的优势 Ÿ   1. 推送数据 HTTP只能从服务器哪里请求数据,除非服务器正在响应客户端请求,否则不能向客户端发送数据.但XMPP连接是双向的,任何一方在任何时候都 ...

  9. Asp.Net多线程用法1

    Asp.Net多线程简单用法 一个web页面 default.aspx 里面有两个控件GridView1,GridView2,通过两个线程分别加载绑定数据. protected void Page_L ...

  10. 继承在WCF中的问题和解决办法

    1. 问题 假设有代码如下: [ServiceContract] public interface IA { [OperationContract] string M1(); } [ServiceCo ...