闭包


JS只有函数作用域,函数外为全局变量,函数内为局部变量
绿圆是函数fn的作用域,在这范围内可访问局部变量b和全局变量a,橙圆是fn内部函数inner的作用域,此范围内可访问自身作用域内的变量c,也可访问父级作用域的变量b,这就形成了一条作用域链
全局空间(蓝圆)为0级作用域,绿圆是1级作用域,橙圆为2级作用域,依次类推
橙圈可访问绿圈或蓝圈内的数据,但蓝圈内访问不了绿圈,绿圈访问不了橙圈,也就是可以一路由内向外访问,但由外到内却不行

若把橙圈拿到绿圈外,蓝圈内的位置,就形成了闭包

现在橙圈也就是函数inner和变量a一样,都是在全局作用域中,并且inner还记得它被定义时所设定的环境,因此它依旧可访问绿圈也就是函数fn的作用域并使用变量b
突破作用域链的途径,升级为全局变量传递(返回)给全局作用域即可

闭包#1

var a = 'global var';
var fn = function () {var b = 'local var';var inner = function () {var c = 'inner';return b;}return inner;
}
var f2 = fn(); //或者直接fn()();
f2();

函数fn中返回了inner,而inner内返回的变量b,现在inner和b都可通过作用域链进行访问

闭包#2

var f2; //函数占位符,这不是必须的,但最好写上
var fn = function () {var b = 'local var';var inner = function () {var c = 'inner';return b;}f2 = inner;
}
fn();
f2();

将函数inner在fn内赋值给全局变量f2,由于inner是在fn内定义的,所以即使该函数后来升级成了全局函数,也依然保留着对fn作用域的访问权

闭包#3

function fn(param) {var inner = function () {return param;}param++;return inner;
}
var f2 = fn(123);
f2();

返回函数被调用时,param++已执行一次,所以f2()结果是124
由此可见,函数绑定的是作用域本身,而不是函数定义时该作用域中的变量或变量当前返回的值

闭包#4循环中的闭包

依次输出0,1,2

function fn() {var arr = [],i;for(i = 0; i < 3; i++){arr[i] = function () {return i;}}return arr;
}
var arr2 = fn();
> arr2[0](); //3 , >表示在控制台输入
> arr2[1](); //3
> arr2[2](); //3

这里创建了三个闭包,都指向一个共同的局部变量i,但闭包不会记录它们的值,它们拥有的只是相关域在创建时的一个引用
对这三个函数任一个而言,当它去获取某个变量时,会从其所在的域开始逐级寻找那个距离最近的i值。由于循环结束时i为3,所以这三个函数都指向了这一共同值

若想输出的是0,1,2,则需要换种闭包形式

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

这里不再直接创建一个返回i的函数,而是将i传递给另一个自调用函数,在该函数中
i被赋值给了局部变量x,这样一来,每次迭代中i的值就能保存在局部变量x中

也可以使用函数声明的方式

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

闭包#5

假设有个变量,表示的是某类特定值,不想将其暴露给外部,因为那样的话其他部分的代码就可能直接修改它,所以需要将其保护在相关函数内部,然后提供getter和setter函数用以访问和设置该变量的值

var getValue,setValue;
(function () {var secret = 0;getValue = function(){return secret;};setValue = function(v){if(typeof v === 'number'){  //假设这里有验证措施secret = v;}};
}())
> getValue(); //0
> setValue(123);
> getValue(); //123

将getter和setter函数放在一个共同的函数中,并在该函数中定义secret变量,使这两函数能共享同一作用域
通过一个自调用函数,在其中定义了全局函数setValue和getValue函数,并以此确保局部变量secret的不可直接访问性

闭包#6迭代器

有时会面对复杂的数据结构,它们通常有着与数组截然不同的序列规则,此时就需要将一些“谁是下一个”的复杂逻辑封装成易于使用的next()函数,然后只需简单地调用next()就能实现相关的遍历操作

function setup(x) {var i = 0;return function () {return x[i++];}
}
var next = setup(['a','b','c']);
> next(); // “a”
> next(); // "b"

在setup()函数中定义一个私有指针i,该指针会始终指向数组的下一个元素

转载于:https://www.cnblogs.com/Grani/p/10514102.html

「JavaScript面向对象编程指南」闭包相关推荐

  1. 「JavaScript面向对象编程指南」原型

    在 JS 中,函数本身也是一个包含了方法(如apply和call)和属性(如length和constructor)的对象,而prototype也是函数对象的一个属性 function f(){} f. ...

  2. 《javascript面向对象编程指南》读书笔记

    <javascript面向对象编程指南>读书笔记 <javascript面向对象编程指南>读书笔记 第一章 面向对象的JavaScript 第二章 基本数据类型与流程控制 变量 ...

  3. 《JavaScript面向对象编程指南》——第1章 引言1.1 回顾历史

    本节书摘来自异步社区<JavaScript面向对象编程指南>一书中的第1章,第1.1节,作者: [加]Stoyan Stefanov 译者: 凌杰 更多章节内容可以访问云栖社区" ...

  4. 《JavaScript面向对象编程指南》——1.3 分析现状

    本节书摘来自异步社区<JavaScript面向对象编程指南>一书中的第1章,第1.3节,作者: [加]Stoyan Stefanov 译者: 凌杰 更多章节内容可以访问云栖社区" ...

  5. 《JavaScript面向对象编程指南》——1.7 训练环境设置

    本节书摘来自异步社区<JavaScript面向对象编程指南>一书中的第1章,第1.7节,作者: [加]Stoyan Stefanov 译者: 凌杰 更多章节内容可以访问云栖社区" ...

  6. 《JavaScript面向对象编程指南》—第128页错误指正

    最近在阅读<JavaScript面向对象编程指南第2版>,感觉很有帮助.今晚发现一个小错误,想指正一下. 如图,书中第128页: 书中的第三个例子代码如下: "potato&qu ...

  7. Javascript面向对象编程指南笔记 - 第三章 - 函数

    第三章 函数 第三章 函数 3-1 什么是函数 3-1-1 调用函数 3-1-2 参数 3-2 预定义函数 3-2-1 parseInt 3-2-2 parseFloat 3-2-3 isNaN 3- ...

  8. JavaScript面向对象编程指南(五) 原型

    第5章 原型 5.1 原型属性 function f(a,b){return a*b;};// length 属性f.length; //2// constructor 构造属性f.construct ...

  9. JavaScript 面向对象编程(三) —— 函数进阶 / 严格模式 / 高阶函数 / 闭包 / 浅拷贝和深拷贝

    本篇为 JavaScript 进阶 ES6 系列笔记第三篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 系列笔记: JavaScript 面 ...

  10. JavaScript 面向对象编程(四) —— 正则表达式

    本篇为 JavaScript 进阶 ES6 系列笔记第四篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 : 系列笔记: JavaScript 面向对象编程(一) -- 面向对 ...

最新文章

  1. 菜鸟学习之linux用户行为日志审计方案
  2. VC对话框全屏显示及相应控件位置改变(转)
  3. 原生JS封装Ajax插件(同域jsonp跨域)
  4. ITK:追加两个3D体积
  5. 创建consumer服务
  6. icinga2 php模块,在Ubuntu 18.04系统上安装Icinga2监视工具的方法
  7. android 表情退格,讯飞输入法Android V8.1.8212 嘘-别说话全套emoji表情上
  8. Version Control
  9. git am 部分发生冲突的处理
  10. asp.net 读取mysql_ASP.NET连接数据库并获取数据
  11. Tableau Desktop 安装与破解
  12. 4场直播丨Oracle、openGauss、易鲸捷EsgynDB
  13. linux主机安装sctp协议栈
  14. 【保研】-- 保研夏令营中线上面试注意事项
  15. java判断字母是否为元音_Java程序来检查字母是元音还是辅音
  16. 智能车寻线算法之北科寻线可能用的方法
  17. 记录CTF命令执行练习中遇到的几道题(一些PHP命令过滤的绕过方法)
  18. 【计算机毕业设计】java ssm网上宠物商店系统
  19. Mybatis最入门---分页查询(逻辑分页与SQL语句分页)
  20. host映射主机名和端口

热门文章

  1. Zookeeper Java客户端搭建
  2. spring源码-第四个后置处理器
  3. 源码专题之spring设计模式:策略模式、原型模式、模板模式
  4. python模拟ssh登录
  5. webpack系列之-原理篇
  6. CentOS 5.11下Oracle 11G R2 Dataguard搭建
  7. 【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
  8. Java Design Demo -简单的队列-异步多任务队列(java android)
  9. 腾讯2014在广州站实习生offer经验(TEG-开发背景)
  10. Android之解决太大太多图片造成的oom