js 闭包 理解 copy
闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
下面就是我的学习笔记,对于Javascript初学者应该是很有用的。
一、变量的作用域
要理解闭包,首先必须理解Javascript特殊的变量作用域。
变量的作用域无非就是两种:全局变量和局部变量。
Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。
var n=999;
function f1(){
alert(n);
}f1(); // 999
另一方面,在函数外部自然无法读取函数内的局部变量。
function f1(){
var n=999;
}alert(n); // error
这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!
function f1(){
n=999;
}f1();
alert(n); // 999
二、如何从外部读取局部变量?
出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。
那就是在函数的内部,再定义一个函数。
function f1(){
var n=999;
function f2(){
alert(n); // 999
}}
在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!
function f1(){
var n=999;
function f2(){
alert(n);
}return f2;
}
var result=f1();
result(); // 999
三、闭包的概念
上一节代码中的f2函数,就是闭包。
各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。
由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。
所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。
四、闭包的用途
闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。
怎么来理解这句话呢?请看下面的代码。
function f1(){
var n=999;
nAdd=function(){n+=1}
function f2(){
alert(n);
}return f2;
}
var result=f1();
result(); // 999
nAdd();
result(); // 1000
在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。
五、使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
六、思考题
如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。
代码片段一。
var name = "The Window";
var object = {
name : "My Object",getNameFunc : function(){
return function(){
return this.name;
};}
};
alert(object.getNameFunc()());
代码片段二。
var name = "The Window";
var object = {
name : "My Object",getNameFunc : function(){
var that = this;
return function(){
return that.name;
};}
};
alert(object.getNameFunc()());
写一个简单的demo组件:
(function(document){
var viewport;
var obj = {
init:function(id){
viewport = document.querySelector("#"+id);
},
addChild:function(child){
viewport.appendChild(child);
},
removeChild:function(child){
viewport.removeChild(child);
}
}
window.jView = obj;
})(document);这个组件的作用是:初始化一个容器,然后可以给这个容器添加子容器,也可以移除一个容器。功能很简单,但这里涉及到了另外一个概念:立即执行函数。 简单了解一下就行。主要是要理解这种写法是怎么实现闭包功能的。
可以将上面的代码结构分成两部分:(function(){}) 和 () , 第1个()是一个表达式,而这个表达式本身是一个匿名函数,所以在这个表达式后面加()就表示执行这个匿名函数。
因此这段代码执行执行过程可以分解如下:
var f = function(document){
var viewport;
var obj = {
init:function(id){
viewport = document.querySelector("#"+id);
},
addChild:function(child){
viewport.appendChild(child);
},
removeChild:function(child){
viewport.removeChild(child);
}
}
window.jView = obj;
};
f(document);在这段代码中似乎看到了闭包的影子,但 f 中没有任何返回值,似乎不具备闭包的条件,注意这句代码:
window.jView = obj;obj 是在 f 中定义的一个对象,这个对象中定义了一系列方法, 执行window.jView = obj 就是在 window 全局对象定义了一个变量 jView,并将这个变量指向 obj 对象,即全局变量 jView 引用了 obj . 而 obj 对象中的函数又引用了 f 中的变量 viewport ,因此 f 中的 viewport 不会被GC回收,会一直保存到内存中,所以这种写法满足闭包的条件。
js 闭包 理解 copy的更多相关文章
- js闭包理解实例小结
Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...
- js闭包理解
js闭包的作用是使函数外可以访问函数内部的变量,是通过 在函数内部 定义 访问函数内变量 的函数实现的,内部的一个函数产生一个闭包 function a() { var i=0; return fun ...
- JS闭包理解_摘
原文地址1:http://www.cnblogs.com/mzwr1982/archive/2012/05/20/2509295.html 闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实 ...
- js闭包理解案例-解决for循环为元素注册事件的问题
转发自http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html 一.变量的作用域 要理解闭包,首 ...
- js闭包理解与使用场景
要理解闭包首先要知道什么是函数的作用域链 因为有函数的作用域链存在,所以函数无论在哪里调用,函数都可以使用函数外部作用域的变量. 当一个函数被调用时,会创建一个执行环境及相应的作用域链.然后使用arg ...
- js 闭包 理解
1.什么是闭包 定义:是指有权访问另一个函数作用域中的变量的函数 创建闭包:在一个函数内部创建另一个函数 基本特点 在返回的匿名函数中 可以调用外部函数的变量 如下例中所示 内部函数(匿名函数) 可以 ...
- js 闭包理解
闭包主要应用于两种情况: 1 函数作为返回值. 2 函数作为参数传递. 第一种举例: function fn(){ var max = 10; return function bar(){ if(x ...
- js闭包之初步理解( JavaScript closure)
闭包一直是js中一个比较难于理解的东西,而平时用途又非常多,因此不得不对闭包进行必要的理解,现在来说说我对js闭包的理解. 要理解闭包,肯定是要先了解js的一个重要特性, 回想一下,那就是函数作用域, ...
- (原创)JS闭包看代码理解
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...
随机推荐
- Nginx深度优化
简介 1.隐藏版本号2.修改Nginx用户与组3.配置Nginx网页缓存时间4.实现Nginx的日志切割5.配置Nginx实现连接超时6.更改进程数7.配置Nginx实现网页压缩功能8.配置Nginx ...
- BOS物流项目第十三天
教学计划 1.Quartz概述 a. Quartz介绍和下载 b. 入门案例 c. Quartz执行流程 d. cron表达式 2.在BOS项目中使用Quartz创建定时任务 3.在BOS项目中使用J ...
- supervisord 进程管家
s supervisor supervisor管理进程,是通过fork/exec的方式将这些被管理的进程当作supervisor的子进程来启动,所以我们只需要将要管理进程的可执行文件的路径添加到sup ...
- 静态html返回
在这篇文章中我们介绍后台路由的概念,后台的路由根据路径返回相应的内容, 首先我们建立一个服务器 let port = 3000 //监听端口let fs = require ('fs')//用来生成可 ...
- 1.5.7、CDH 搭建Hadoop在安装之前(定制安装解决方案---配置单用户模式)
配置单用户模式 在传统的Cloudera Manager部署中,管理每台主机上的Hadoop进程的Cloudera Manager Agent以root用户身份运行.但是,某些环境会限制对root帐户 ...
- cakePHP模型内置回调函数afterFind()的使用。
在用find获取数据后,我们要对所获取到的数据做一些处理,这时,直接在模型层覆盖cakephp内置的回调函数,使用find时会自动调用. 其中$baomings 就是find 到的 $this-> ...
- node 加载逻辑
[node 加载逻辑] require(X) from module at path Y . If X is a core module, a. return the core module b. S ...
- cdh 安装系列1-- manager 6.01 安装
一.如果是远程centos安装,请参考我得博客,通过xmanager 链接centos桌面 二.Non-production Installation Ideal for trying Clouder ...
- 如何正确获取MYSQL的ADO连接字符串
首先你正确安装了MYSQL的数据库驱动程序(mysql-connector-odbc-5.3.2-win32.msi )http://dev.mysql.com/downloads/connector ...
- as3.0加载swf并控制
私人QQ 280841609 var myload:Loader=new Loader(); var url:URLRequest=new URLRequest("1.swf"); ...
