javascript 中的暗物质 - 闭包

1. 诡异的闭包

javascript 中有一个特殊的特性 - 闭包,对于 .NET 程序员来说,比较熟悉的是面向对象的程序设计 OOP,  而来自函数式语言的闭包则显得比较诡异,许多程序员对它敬而远之。

对于闭包我们还是要从函数式语言的特点说起。

不知道你有没有发现,在 javascript 中没有 public ,private 之类的关键字,也没有 class ,虽然也存在对象一说,但是对象的地位远远没有在 C# 中是一等公民,在 js 中,没有对象你也可以一样写程序。它只是一种数据的表示形式而已,可有也可无。

2. 闭包何来?

如何在 javascript 实现数据的保护呢?闭包就是实现它的利器,这需要我们放下普通的对象,理解一下 javascript 的工作原理。

在 javascript 中,可以在函数中定义新的函数,这种嵌套函数还可以作为函数的返回值,被外部的变量所引用。在普通的程序设计语言中,比如 C 中,虽然也存在函数指针的概念,但是,所谓的函数指针仅仅是一段代码的地址而已,而 javascript 中返回的函数引用,则不限于此。

在 C 语言中,在函数运行的时候,局部变量是保存在堆栈中的,函数执行完毕,系统所做的是弹出堆栈。

实际上,在 javascript 中,函数每次执行的时候,注意是运行时,系统会同时创建一个此次函数运行的环境对象,而此次运行期间的局部变量则关联在这个环境对象上,在普通不返回函数的普通函数中,函数执行完毕,则环境对象也一起释放。而如果函数返回了定义在外部函数中的嵌套函数,那么,这个环境对象将不会释放,也就是说,这个时候,返回了一个看得见的函数对象,还附带了一个看不见的暗物质 - 外部函数的环境对象。

看得见的函数对象加上隐含的环境对象就是闭包。

这个环境对象只能通过这个函数隐式访问,我们并没有它的引用,也无法直接访问它。结果就是实现了信息的隐藏。

3. 实现私有的数据

考虑下面的代码

function outer() {var name = "Alice";var inner = function () {return name;}return inner;
}var fn = outer();
alert(fn());

在这个例子中,看起来简单的 fn 函数背后,其实暗藏了在执行 outer 函数时期创建的环境对象,所以通过 fn 可以得到 Alice 这个名字,而且没有其他的渠道允许得到这个名字。

4. 为什么数据搞乱了?

再看另外一个经典的例子。

<body><div><a href="#">Click Me!</a><a href="#">Click Me!</a><a href="#">Click Me!</a></div><script type ="text/javascript">function main(links) {for (var i = 0; i < links.length; i++) {links[i].onclick = function () {alert(i + 1);}}};main(document.getElementsByTagName("a"));</script></body>

弹出的是多少呢?感觉有三次循环,应该弹出 1, 2, 3。运行一下,你会看到实际上是 4, 4, 4!

是不是非常诡异?

从闭包的角度来说,则非常简单,main 函数执行了几次呢?只有一次,在执行的时候创建了一个闭包对象,其中引用了定义在 main 中的局部变量 i,在循环体中,实际上创建了三个内部函数,它们引用的都是同一个环境对象。这些函数注册到链接的 onclick 事件上,其实也就是已经传出了函数 main,所以,main 的环境对象也就悄悄地成为了暗物质,而循环完成之后 i 已经最终被赋予了 3 这个值,三个函数访问的是同一个环境对象中的 i, 所以,在点击链接的时候看到 4 这个结果也就正常了。

5. 解铃还需系铃人

如果希望得到的是  1, 2, 3 这个结果又该怎么办呢?

我们可以定义一个内部函数,让它执行三次,这个将会创建三个对应的环境对象,我们可以使得这三个环境对象包含不同的值。

function main(links) {var inner = function (elem, i) {elem.onclick = function () {alert(i + 1);};};for (var i = 0; i < links.length; i++) {var elem = links[i];inner(elem, i);}
};

由于 inner 函数执行了三次,所以将会创建三个不同的环境对象,每个环境对象中的  i 都是不同的值。这样注册到 onclick 中的函数就可以访问到不同的值了。

闭包的概念

这里,我们可以看一下闭包的概念了。来自 Wiki 的说明是这样的

在计算机科学中,闭包Closure)是词法闭包Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。

转载于:https://www.cnblogs.com/haogj/archive/2012/11/28/2793535.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/296315.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

女友的前男友教我该怎么照顾她......

1 总觉得不太对劲&#xff1f;▼2 这回复真神&#xff01;&#xff08;via.机智得赵老师&#xff09;▼3 奇怪的知识又增加了......▼4 卖茶小妹真的与时俱进...&#xff08;via.外卖小哥金城武&#xff09;▼5 超大容量花露水没见过吧&#xff1f;&#xff01;▼6 别人在…

笔记本电脑优化指南

一、旧本本运行如飞的八大绝技 由于目前技术以及其他因素的限制&#xff0c;主流配置的笔记本电脑仍然与主流台式机的性能有相当的差距&#xff0c;这差距表现出来的就是流畅度&#xff0c;但是只要优化得当&#xff0c;二手本本也能运行如飞&#xff01;1.系统优化首先&#…

浅谈.Net异步编程的前世今生----EAP篇

前言在上一篇博文中&#xff0c;我们提到了APM模型实现异步编程的模式&#xff0c;通过使用APM模型&#xff0c;可以简化.Net中编写异步程序的方式&#xff0c;但APM模型本身依然存在一些缺点&#xff0c;如无法得知操作进度&#xff0c;不能取消异步操作等。针对这些缺点&…

java sql objects_Java SQL注入学习笔记

1 简介文章主要内容包括&#xff1a;Java 持久层技术/框架简单介绍不同场景/框架下易导致 SQL 注入的写法如何避免和修复 SQL 注入2 JDBC介绍JDBC&#xff1a;全称 Java Database Connectivity是 Java 访问数据库的 API&#xff0c;不依赖于特定数据库 ( database-independent …

CentOS 5 CentOS 6 启动流程及关键步骤

CentOS 5 CentOS 6 启动流程及关键步骤1、加电自检&#xff1a;当打开主机电源时&#xff0c;主机会唤醒cpu&#xff0c;使其运行CMOS中的BIOS&#xff0c;BIOS检查必要的硬件是否存在&#xff08;内存、硬盘等&#xff09;BIOS其实是一个小型系统&#xff0c;可以完成一些像检…

有关坐标系常见问题的问与答(转自ESRI中国社区)

本帖最后由 来生缘 于 2012-11-24 20:56 编辑坐标系是gis的灵魂&#xff0c;坐标系问题在桌面版是个永恒的主题&#xff0c;下面将常见的坐标系问题以问答的形式列出来&#xff0c;希望对大家有所帮助。问&#xff1a;我这有2个不同坐标的shp要素&#xff0c;这2个要素是同一地…

建议给予导师决定硕博士能否毕业的自主权?教育部:将充分采纳!

全世界只有3.14 % 的人关注了爆炸吧知识本文来源&#xff1a;教育部 近日&#xff0c;教育部官网公布了《对十三届全国人大三次会议第9546号建议的答复》。针对人大代表提出的关于完善高校研究生科研成果评价标准的建议&#xff0c;教育部作出回应&#xff0c;并透露了对加强研…

浅谈.Net异步编程的前世今生----APM篇

前言在.Net程序开发过程中&#xff0c;我们经常会遇到如下场景&#xff1a;编写WinForm程序客户端&#xff0c;需要查询数据库获取数据&#xff0c;于是我们根据需求写好了代码后&#xff0c;点击查询&#xff0c;发现界面卡死&#xff0c;无法响应。经过调试&#xff0c;发现查…

java打印三角形_java基础打印三角形

/*实心等边三角形*/class Triangle2{public static void main(String[] args){int n5;for(int i0;ifor(int j0;jSystem.out.print(" ");}for(int k0;kSystem.out.print("*");}System.out.println();}}}/*输出&#xff1a;**************************//*空心…

浅谈.Net异步编程的前世今生----TPL篇

前言我们在此前已经介绍了APM模型和EAP模型&#xff0c;以及它们的优缺点。在EAP模型中&#xff0c;可以实时得知异步操作的进度&#xff0c;以及支持取消操作。但是组合多个异步操作仍需大量工作&#xff0c;编写大量代码方可完成。因此&#xff0c;在.Net Framework 4.0中&am…

Google:推荐几款好用的Chrome浏览器插件

1、Clear Cache 0.3.3.2 一键清空浏览器缓存数据。 https://chrome.google.com/webstore/detail/clear-cache/cppjkneekbjaeellbfkmgnhonkkjfpdn 2、Pig Toolbox 1.0.6.4 双击关闭页签&#xff0c;鼠标手势&#xff0c;手势动作轮&#xff0c;摇臂&#xff0c;超级拖拽&#xf…

豆瓣8.7!BBC这部成人社会禁片,曝光了行业内不能说的秘密

全世界只有3.14 % 的人关注了爆炸吧知识不知道生活中的你是否也会这样&#xff1f;平时即便不买东西&#xff0c;没事也会打开淘宝看看。但凡遇上双11、618各种促销节&#xff0c;总觉得不买好像就亏了&#xff0c;每每忍不住手痒&#xff0c;交了一堆智商税之后又开始后悔。如…

qt4.7 mysql_详解Qt 4.7编译和访问Mysql驱动

Qt4.7编译和访问Mysql驱动是本文要介绍的内容&#xff0c;不多说&#xff0c;我们来看内容。今天摸索了一上午&#xff0c;终于用qt连上mysql了1、安装一个mysql5.0以上版本***要求&#xff1a;(1)安装路径不要有空格和点(2)不要默认安装&#xff0c;选自定义安装&#xff0c;保…

Lync-用户-电话号码-更新

1. 更新-用户-手机号 2. 服务器-更新-地址簿 3. 客户端-更改-注册表-<只操作一次!> 在命令提示符中输入如下命令&#xff1a; Reg Add HKLM\Software\Policies\Microsoft\Communicator /v GalDownloadInitialDelay /t REG_DWORD /d 0 /f 4. 客户端-删除-用户信息 退出-Ly…

读取数量不定的输入数据

对于整数求和&#xff0c;我们经常用循环来求所求数个数确定的一组数。可是&#xff0c;我们预先不知道要对多少个数求和&#xff0c;这就需要不断读取数据直至没有新的输入为止&#xff1a; #include<iostream> int main() {int sum 0, value 0;// sum为出入数字的和&…

AdBlock屏蔽网易的“我来挑错”和“转发至微博”

今天我的AdBlock出错了&#xff0c;重装了下后发现以前的一些自定义的配置丢了&#xff0c;其中一个比较常用的就是屏蔽网易的的"我来挑错"和"转发至微博"两个按钮&#xff0c;便重新分析了一下&#xff0c;找到了屏蔽方法。这里记录一下&#xff0c;以备后…

豆瓣评分9分+,6部经典趣味数学纪录片!

全世界只有3.14 % 的人关注了爆炸吧知识数学是研究数量、结构、变化以及空间模型等概念的一门学科。透过抽象化和逻辑推理的使用&#xff0c;由计数、计算、量度和对物体形状及运动的观察中产生。数学家们拓展这些概念&#xff0c;为了公式化新的猜想以及从合适选定的公理及定义…

C# Hook原理及EasyHook简易教程

前言在说C# Hook之前&#xff0c;我们先来说说什么是Hook技术。相信大家都接触过外挂&#xff0c;不管是修改游戏客户端的也好&#xff0c;盗取密码的也罢&#xff0c;它们都是如何实现的呢&#xff1f;实际上&#xff0c;Windows平台是基于事件驱动机制的&#xff0c;整个系统…

java当前时间推前三个月_获取当前时间的前三个月 java

java获取当前路径的几种方法 1、利用System.getProperty()函数获取当前路径&#xff1a; System.out.println(System.getProperty("user.dir"));//user.dir指定了当前的路径 2、使用File提供的函数获取当前路径&#xff1a; File directory new File(""…

squid 服务器的应用

实验名称&#xff1a;squid 服务器的应用 实验目标&#xff1a; 任务一&#xff1a;实现正向代理 任务二&#xff1a;实现透明代理 任务三&#xff1a;实现反向代理 提示1、在启动squid服务程序之前需要先确认Linux主机具有完整的域名&#xff0c;如果没有可以在hosts文件中进行…