下面是我对闭包的理解:(把他们整理出来,整理的过程也是在梳理)

参考《JavaScript面向对象编程指南》

1.首先,在理解闭包之前:

我们首先应该清楚下作用域和作用域链

作用域:每个函数定义时创建时自己的环境即作用域

作用域链:函数内可访问自身和父级作用域中的变量,函数外不可访问函数内的私有变量

var a = 1;
function f(){
var b = 1;
return a;
}
f(); /*1*/
b; ReferenceError: b is not defined

  

解读——在这里,变量a是属于全局域的,变量b的作用域就在函数f()内了,所以:

  在f()内,a和b都是可见的;

  在f()外,a是可见的,b则不可见;

下面是我在控制台中测试:可以更好的理解作用域链。

在outer内定义了另一个函数inner;那么在inner()中可以访问的变量既可以来自他自身的作用域,也可以来自其“父级”的作用域。这就形成了一条作用域链,该链的长度取决于我们的需要。


接下来我们就可以正式认识下闭包了:

我们通过闭包来突破作用域链的过程,也许你会发现其中的乐趣

首先我们看下下面的代码:

var a = 'global variable';
var F = function(){
var b = "local variable";
var N = function(){
var c = "inner local"
}
}

  

我们假想全局作用域为G,我们可以将其视为可以包含一切的宇宙

然后就是本地空间F

在F内部,还有F的私有内部空间N

  当我们将N的空间扩展到F以外,并且止步于全局空间内部时,就产生了一种有趣的东西    ——闭包

闭包#1

var a = 'global variable';
var F = function(){
var b = "local variable";
var N = function(){
var c = "inner local";
return b;
};
return N;
}; b; /*ReferenceError : b is not defined*/
/*函数F中包含了局部变量b,因此后者在全局空间里是不可见的。*/
N() /*ReferenceError : N is not defined*/
/*很明显当我们在全局中调用私有函数N时,会抛出错误;我们在全局中是无法访问一个函数内的私有函数的*/

  

上面的代码在控制台中的返回结果:

接下来,我们在控制台中完整正确测试一次:

解读——

  首先,在函数N()内部,我们是可以获得b的返回值的(来自于他的父级作用域,可以访问到),我们将b的值作为私有函数N的返回值(如果这时我们在全局中调用函数N(),仍然会抛出错误,见上面代码)

  于是,我们要解决这个问题。将F的私有函数N作为F()的返回值。

  接着,把函数F()赋值给另一个全局变量(实际上是将F()的返回值赋值给了一个全局变量)

  从而生成了一个可以访问F()私有空间的全局函数。

var inner = F();
inner (); /*local variable*/

  

闭包#2

下面这种方法结果与之前相同,实现上略有不同。

在这里F()不在返回函数了,而是直接在函数体内创建了一个新的全局函数inner()

(首先:我们声明一个占位符,尽管这不是必须的,最好还是声明一下)

var inner;        //placeholder 占位符
var F = function () {
var b = "local variable";
var N = function () {
return b;
};
inner = N ;
};

  

现在我们可以自行测试一下

F();          /*undefined*/        /*想想????为什么*/
inner(); /*local variable*/

  

解读——

  我们在函数F()内定义了一个新的函数N(),并将它赋值给全局变量inner。

  由于N()是在F()内定义的,他可以访问到F()的作用域,所以即使该函数后来升级成了全局函数,但是它依然可以保留对F()作用域的访问权

相关定义与闭包#3

接下来,我们这次使用函数参数。

我们在这里创建了一个函数,该函数将返回一个子函数,而这个子函数返回的则是父函数的参数

function F(param) {
var N = function () {
return param;
};
param++;
return N;
};

  

我在控制台中做了测试:

发现:返回函数被调用时,param++已经执行过一次递增操作了,inner()返回的是更新后的值

由此我们可以看出:函数绑定的是作用域本身(!*!)而不是在函数定义时该作用域中的变量或当前变量所返回的值

  

循环中的闭包#4

我们首先看一下新手在闭包中容易犯的错误:

function F() {
var arr = [], i ;
for (i=0;i<3;i++){
arr[i] = function () {
return i;
};
}
return arr;
}
arr[0](); /*3*/
arr[1](); /*3*/
arr[2](); /*3*/

  

显然这并不是我们想要的结果:

  在这里,我们创建了三个闭包,而三个闭包都指向了一个共同的局部变量i,

  但是闭包并不会记录他们的值,他们所拥有的的只是相关作用域在创建时的一个连接(即引用)

  在这个例子中,变量i恰巧存在于定义这三个函数域中。对这三个函数中的任何一个而言,当他要去获取某个值时,他会从其所在的域开始逐级寻找那个距离最近的i值。由于循环结束时i的值为3,所以这三个函数都指向了一个共同值。

我们换一种闭包的形式:(来解决这个问题)

 function F(){
var arr = [], i ;
for (i=0;i<3;i++){
arr[i] = (function (x) {
return function () {
return x;
}
} (i));
}
return arr;
} var arr = F();
arr[0]() /*0*/
arr[1]() /*1*/
arr[2]() /*2*/

  

我们还可以定义一个正常点的函数 (不使用即时函数)来实现相同的功能。

要点是在每次迭代中,我们要在中间函数内将i的值“本地化”

function F() {
  function binder(x){ 
return function () {
return x;
}
}
var arr = [], i ;
for (i=0;i<3;i++){
arr[i] = binder(x);
}
return arr;
} var arr = F()
arr[0]() /**/
arr[1]() /**/
arr[2]() /**/

最后,希望自己在实战中深入理解闭包的巧妙和乐趣。

(知识点)JavaScript闭包的更多相关文章

  1. javascript闭包的理解

    闭包是Javascript的一个难点,但也是一个很重要的知识点. 1.首先我们要知道变量作用域链 变量的作用域分两种:全局变量和局部变量.没有定义到任何函数中的变量为全局变量,在函数中定义的变量为局部 ...

  2. JavaScript 闭包小记

    最近朋友面试被问到了 JS 闭包的问题,本人一时语塞,想起了袁华的一句话:“这道题太难了,我不会做,不会做啊!”. JS 闭包属于面向对象的一个重要知识点,特此本人又开始了一段说走就走的旅程. 闭包就 ...

  3. JavaScript闭包应用的整理

    0 什么是JavaScript闭包? 当函数定义内部的函数被保存到外部时,就会形成闭包.闭包会导致作用域链不释放,造成内存泄漏. 1 获取局部变量 [练习目的] 下面这个练习,是为了通过闭包实现获取定 ...

  4. 那些年,我们误解的 JavaScript 闭包

    说到闭包,大部分的初始者,都是谈虎色变的.最近对闭包,有了自己的理解,就感觉.其实我们误解闭包.也被网上各种说的闭包的解释给搞迷糊. 一句话:要想理解一个东西还是看权威的东西. 下面我来通俗的讲解一个 ...

  5. 《Web 前端面试指南》1、JavaScript 闭包深入浅出

    闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...

  6. JavaScript 闭包深入浅出

    闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...

  7. JavaScript闭包(Closure)

    JavaScript闭包(Closure) 本文收集了多本书里对JavaScript闭包(Closure)的解释,或许会对理解闭包有一定帮助. <你不知道的JavsScript> Java ...

  8. Javascript闭包和C#匿名函数对比分析

    C#中引入匿名函数,多少都是受到Javascript的闭包语法和面向函数编程语言的影响.人们发现,在表达式中直接编写函数代码是一种普遍存在的需求,这种语法将比那种必须在某个特定地方定义函数的方式灵活和 ...

  9. javascript闭包理解

    //闭包理解一 function superFun(){ var _super_a='a'; function subfuc(){ console.log(_super_a); } return su ...

随机推荐

  1. 1588: [HNOI2002]营业额统计

    1588: [HNOI2002]营业额统计 Time Limit: 5 Sec  Memory Limit: 162 MBSubmit: 9203  Solved: 3097[Submit][Stat ...

  2. 快速上手UIBezierPath

    UIBezierPath主要用来绘制矢量图形,它是基于Core Graphics对CGPathRef数据类型和path绘图属性的一个封装,所以是需要图形上下文的(CGContextRef),所以一般U ...

  3. 无线同步模块SYN1000在电力监测相位测量领域的应用方案

    在电力监测领域,出于安全考虑,有些系统不得不采用无线通信的方式,在这样一个无线通信的应用系统,该如何来控制多个设备进行同步采样,以期提高相位角的测量精度,是一个不小的难题. 很多技术人员习惯性的采用无 ...

  4. 第37篇 Asp.Net源码解析(二)--详解HttpApplication

    这篇文章花了点时间,差点成烂到电脑里面,写的过程中有好几次修改,最终的这个版本也不是很满意,东西说的不够细,还需要认真的去看下源码才能有所体会,先这样吧,后面有时间把细节慢慢的再修改.顺便对于开发的学 ...

  5. css的存在方式和选择器

    css的存在方式 元素内联 页面嵌入 外部引入 元素内联 直接在html的标签中定义样式,类似于: <div style="属性1;属性2;属性3"><div&g ...

  6. pymysql使用心得记录

    -----------更新日志 16.7.29------------- (该记录对应文章<豆瓣电影Top250基本信息抓取  >) 折腾了将近两天才把mysql数据库功能给实现了. 经过 ...

  7. 任务调度之集群(基于Quartz.net)

    上一篇我们完成了任务调度的持久化,传送门:任务调度之持久化(基于Quartz.net) 这篇我们来完成Quartz.net的一个比较优秀的功能,即集群:集群可以提高任务调度服务的容灾性, 当一个节点宕 ...

  8. Tcl与Design Compiler (九)——综合后的形式验证

    本文属于原创手打(有参考文献),如果有错,欢迎留言更正:此外,转载请标明出处 http://www.cnblogs.com/IClearner/  ,作者:IC_learner 这里来讲一下forma ...

  9. dellR720重启找不到启动引导项,手动选择也无用。

    机器重启后显示 no boot device available.(如下图)检查bios中设置也是没问题的,因为装完系统后根本没动过什么.F11手动选择启动项也还是会跳到这里来. 这台机子做的Raid ...

  10. javaEE与JSP基础

     JSP基础 1. jsp的作用:  * Servlet:    > 缺点:不适合设置html响应体,需要大量的response.getWriter().print("<html ...