什么是闭包?


先看一段代码:

function a(){
var n = 0;
function inc() {
n++;
console.log(n);
}
inc();
inc();
}
a(); //控制台输出1,再输出2

简单吧。再来看一段代码:

function a(){
var n = 0;
this.inc = function () {
n++;
console.log(n);
};
}
var c = new a();
c.inc(); //控制台输出1
c.inc(); //控制台输出2

简单吧。

什么是闭包?这就是闭包!

有权访问另一个函数作用域内变量的函数都是闭包。这里 inc 函数访问了构造函数 a 里面的变量 n,所以形成了一个闭包。

再来看一段代码:

function a(){
var n = 0;
function inc(){
n++;
console.log(n);
}
return inc;
}
var c = a();
c(); //控制台输出1
c(); //控制台输出2

看看是怎么执行的:

var c = couter(),这一句 couter()返回的是函数 inc,那这句等同于 var c = inc;

c(),这一句等同于 inc();  注意,函数名只是一个标识(指向函数的指针),而()才是执行函数。

后面三句翻译过来就是:  var c = inc;  inc();  inc();,跟第一段代码有区别吗? 没有。

什么是闭包?这就是闭包!

所有的教科书教程上都喜欢用最后一段来说明闭包,但我觉得这将问题复杂化了。这里面返回的是函数名,没看过谭浩强C/C++程序设计的同学可能一下子没反应出带不带()的区别,也就是说这种写法自带一个陷阱。虽然这种写法更显高大上,但我还是喜欢将问题单一化,看看代码 1 和代码 2,你还会纠结函数的调用,你会纠结 n 的值吗?

为啥要这样写?


我们知道,js的每个函数都是一个个小黑屋,它可以获取外界信息,但是外界却无法直接看到里面的内容。将变量 n 放进小黑屋里,除了 inc 函数之外,没有其他办法能接触到变量 n,而且在函数 a 外定义同名的变量 n 也是互不影响的,这就是所谓的增强“封装性”。

而之所以要用 return 返回函数标识 inc,是因为在 a 函数外部无法直接调用 inc 函数,所以 return inc 与外部联系起来,代码 2 中的 this 也是将 inc 与外部联系起来而已。

常见的陷阱


看看这个:

function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
var funcs = createFunctions();
for (var i=0; i < funcs.length; i++){
console.log(funcs[i]());
}

乍一看,以为输出 0~9 ,万万没想到输出10个10?

这里的陷阱就是:函数带()才是执行函数! 单纯的一句 var f = function() { alert('Hi'); }; 是不会弹窗的,后面接一句 f(); 才会执行函数内部的代码。上面代码翻译一下就是:

var result = new Array(), i;
result[0] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
result[1] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
...
result[9] = function(){ return i; }; //没执行函数,函数内部不变,不能将函数内的i替换!
i = 10;
funcs = result;
result = null; console.log(i); // funcs[0]()就是执行 return i 语句,就是返回10
console.log(i); // funcs[1]()就是执行 return i 语句,就是返回10
...
console.log(i); // funcs[9]()就是执行 return i 语句,就是返回10

为什么只垃圾回收了 result,但却不收了 i 呢? 因为 i 还在被 function 引用着啊。好比一个餐厅,盘子总是有限的,所以服务员会去巡台回收空盘子,但还装着菜的盘子他怎么敢收? 当然,你自己手动倒掉了盘子里面的菜(=null),那盘子就会被收走了,这就是所谓的内存回收机制。

至于 i 的值怎么还能保留,其实从文章开头一路读下来,这应该没有什么可以纠结的地方。盘子里面的菜,吃了一块不就应该少一块吗?

总结一下


闭包就是一个函数引用另外一个函数的变量,因为变量被引用着所以不会被回收,因此可以用来封装一个私有变量。这是优点也是缺点,不必要的闭包只会徒增内存消耗!另外使用闭包也要注意变量的值是否符合你的要求,因为他就像一个静态私有变量一样。闭包通常会跟很多东西混搭起来,接触多了才能加深理解,这里只是开个头说说基础性的东西。

转载自  http://www.cnblogs.com/qieguo/p/5457040.html

带你一分钟理解闭包--js面向对象编程(转载他人)的更多相关文章

  1. 带你一分钟理解闭包--js面向对象编程

    上一篇<简单粗暴地理解js原型链--js面向对象编程>没想到能攒到这么多赞,实属意外.分享是个好事情,尤其是分享自己的学习感悟.所以网上关于原型链.闭包.作用域等文章多如牛毛,很多文章写得 ...

  2. 理解闭包--js面向对象编程

    什么是闭包? 先看一段代码: function a(){ var n = 0; function inc() { n++; console.log(n); } inc(); inc(); } a(); ...

  3. 简单粗暴地理解js原型链–js面向对象编程

    简单粗暴地理解js原型链–js面向对象编程 作者:茄果 链接:http://www.cnblogs.com/qieguo/archive/2016/05/03/5451626.html 原型链理解起来 ...

  4. JS面向对象编程(进阶理解)

    JS 面向对象编程 如何创建JS对象 JSON语法声明对象(直接量声明对象) var obj = {}; 使用 Object 创建对象 var obj = new Object(); JS对象可以后期 ...

  5. Js面向对象编程

    Js面向对象编程 1.     什么是面向对象编程? 我也不说不清楚什么是面向对象,反正就那么回事吧. 编程有时候是一件很快乐的事,写一些小游戏,用编程的方式玩游戏等等 2.     Js如何定义一个 ...

  6. js原生设计模式——3简单工厂模式\js面向对象编程实例

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  7. 页面循环绑定(变量污染问题),js面向对象编程(对象属性增删改查),js字符串操作,js数组操作

    页面循环绑定(变量污染问题) var lis = document.querySelectorAll(".ul li") for ( var i = 0 ; i < lis. ...

  8. js面向对象编程 ---- 系列教程

    原 js面向对象编程:数据的缓存 原 js面向对象编程:如何检测对象类型 原 js面向对象编程:if中可以使用那些作为判断条件呢? 原 js面向对象编程:this到底代表什么?第二篇 原 js面向对象 ...

  9. 初步理解require.js模块化编程

    初步理解require.js模块化编程 一.Javascript模块化编程 目前,通行的Javascript模块规范共有两种:CommonJS和AMD. 1.commonjs 2009年,美国程序员R ...

随机推荐

  1. 三种记录 MySQL 所执行过的 SQL 语句的方法

    程式 Debug 有時後從前面第一行追起來很辛苦(程式碼太多或 compile 過), 另一種做法就是從後面追起來, 反正最後寫入的是 DB, 那就從 DB 開始往前推, 所以就是要抓程式是執行哪些 ...

  2. hdu 3547 DIY Cube (Ploya定理)

    DIY Cube Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total S ...

  3. Atcoder ABC 069 C - 4-adjacent D - Grid Coloring

    C - 4-adjacent Time limit : 2sec / Memory limit : 256MB Score : 400 points Problem Statement We have ...

  4. LINUX中,find结合grep正则表达式,快速查找代码文件。

    ###目的###LINUX中,find结合grep正则表达式快速查找代码. 例如经常有需求:查找当前目录下所有.h文件中,"public开头,中间任意字符,以VideoFrameReceiv ...

  5. Vue中对data的操作

    1. {{a}} var vm = new Vue({ el: '#app', data: { a: { a: 1, b: 2 } } })   vm.a.c = 'sadoisad' // 按理说是 ...

  6. javaScript for in循环遍历对象

    for循环常被我们用来遍历数组,而如何遍历对象呢? 这时就需要用到for in循环了 写一个遍历对象名简写如下: for(var xxx in ooo){console.log(xxx)} 其中xxx ...

  7. layui图片懒加载-loading占位图

    前言 使用layui的图片懒加载,发现未加载的图片没有loading占位图,显示的是裂图,看着不是很好.找了一些解决方法我统一记录一下. layui图片懒加载使用方法 layui.use(’flow’ ...

  8. android+myeclipse+mysql自定义控件下拉框的数据绑定

    原创作品,允许转载,转载时请务必声明作者信息和本声明.http://www.cnblogs.com/zhu520/p/8031936.html 本人小白,那个大神看到有问题可指出,谢谢.... 这个是 ...

  9. 【Henu ACM Round#17 C】Kitahara Haruki's Gift

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 判断sum/2这个价值能不能得到就可以了. 则就是一个01背包模型了. 判断某个价值能否得到. f[j]表示价值j能否得到. f[0 ...

  10. ArcGIS Engine能够做什么?

    转自原文ArcGIS Engine能够做什么? ArcGIS Engine是一组跨平台的嵌入式ArcObjects,它是ArcGIS软件产品的底层组件,用来构建定制的GIS和桌面制图应用程序,或是向原 ...