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)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法 认为闭包是由函数和与其相关的引用环境组合而成的实体。
相关推荐
5. **应用场景**:闭包在JavaScript中有很多实用的应用,如模块化开发(通过闭包封装变量和方法)、数据隐私保护(隐藏内部变量)、记忆化(缓存计算结果,提高性能)以及异步操作(例如事件处理和定时器)等。...
闭包是一种强大的编程概念,它允许函数访问和操作其外部作用域中的变量,即使在函数执行完毕后仍然能够保持对这些变量的访问。这个特性使得闭包成为函数式编程和面向对象编程中的重要工具,尤其是在诸如Lisp、...
理解闭包对于编写高效的 JavaScript 代码至关重要,因为它可以帮助开发者管理作用域、创建私有变量以及在异步操作中保持状态。正确使用闭包可以避免全局变量污染,提高代码的可维护性和性能。在实际编程中,闭包经常...
通过上述对闭包和垃圾回收机制的详细介绍,我们可以看出这两个概念在JavaScript编程中的重要性和复杂性。正确理解和运用闭包可以极大地提升代码质量和性能;而了解垃圾回收机制则有助于编写更高效、更安全的代码。
在JavaScript中,闭包是一种特殊的函数,它能够访问并操作其定义时的作用域,即使该作用域在其执行时已经不存在。闭包的概念是基于JavaScript的词法作用域规则,即函数内部可以访问到外部的变量,但外部不能直接访问...
JavaScript中的闭包是一种高级特性,它是函数和其周围状态(词法作用域)的组合,即使函数在其定义的作用域之外被调用,它仍然能访问到这些状态。这个概念是JavaScript编程中的核心部分,尤其在处理异步操作、模块化...
闭包的特性使得它在JavaScript中既神秘又强大。 首先,我们从闭包的定义谈起。在JavaScript中,函数可以被看作是一等公民(first-class citizens),这意味着函数可以像任何其他对象一样被创建、传递、返回或者赋值...
该控件使用JavaScript闭包原理,编写一个通用的Web图片浏览类,使用CSS控制显示外观,在网页中调用ShowPhotoLib类构造一个图片浏览器的对象,对图片进行控制显示。 4. 实现方法 实现ShowPhotoLib类,需要使用...
在了解闭包的同时,也需要理解JavaScript中的对象属性名解析机制。对象属性名解析是指JavaScript如何在对象上查找并获取属性的过程。JavaScript中的对象有两种类型:“本地对象”和“宿主对象”。本地对象属于语言的...
5. 对象与原型:理解JavaScript中的对象创建和原型链,以及如何通过原型实现继承。 二、JavaScript高级特性 1. 异步编程:学习事件循环、回调函数、Promise和async/await,掌握异步处理方法。 2. 模块化:了解...
4. **模块模式**:在JavaScript中,闭包常用于创建私有变量和方法,实现类似类的封装。通过闭包,可以创建只暴露特定接口的模块,隐藏内部实现细节。 5. **异步编程**:在处理异步操作如定时器或事件处理时,闭包...
JavaScript中的闭包是一种重要的编程概念,它涉及到函数、作用域和变量持久化等多个核心知识点。闭包的本质是函数能够访问并操作其外部作用域内的变量,即使在其外部作用域已经结束之后仍然能保持对这些变量的访问。...
从给定的文件信息来看,主要围绕JavaScript中的闭包概念进行深入探讨。闭包是计算机科学中一个重要的概念,特别是在函数式编程语言中,如JavaScript,它允许一个函数在定义时捕获并记住其周围的环境状态,即使在函数...
JavaScript中的闭包是一种非常重要的概念,它在编程中起着至关重要的作用,特别是在函数式编程和模块化设计中。闭包本质上是函数能够记住并访问其词法作用域内变量的能力,即使该函数在其词法作用域外部被调用。在...
JavaScript 闭包是编程语言中的一个关键概念,尤其在JavaScript中有着重要的应用。它涉及到函数、作用域和变量持久化等多个方面,对于提升代码质量、实现模块化以及优化内存管理至关重要。本文将深入探讨JavaScript...
Python中的闭包是一种重要的编程概念,它涉及到函数、作用域和引用等核心概念。闭包在Python编程中扮演着至关重要的角色,特别是在处理高阶函数、数据封装和异步编程等场景。以下是对闭包的详细解释: 1. **闭包的...
JavaScript中的闭包是一种重要的编程概念,它涉及到函数、作用域和变量持久化等多个核心知识点。在深入理解闭包之前,我们需要先了解JavaScript的作用域规则。 1. **作用域**:在JavaScript中,变量的作用域分为两...
Javascript中有几个非常重要的语言特性——对象、原型继承、闭包。其中闭包 对于那些使用传统静态语言C/C 的程序员来说是一个新的语言特性。本文将以例子入手来介绍Javascript闭包的语言特性,并结合...
JavaScript中的函数闭包是一个重要的概念,它涉及到函数的作用域、变量持久化以及内存管理等多个方面。闭包的本质是在函数内部创建另一个函数,使得内部函数能够访问并操作外部函数的局部变量,即使外部函数已经执行...