「JavaScript面向对象编程指南」闭包
闭包

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,该指针会始终指向数组的下一个元素
「JavaScript面向对象编程指南」闭包的更多相关文章
- 「JavaScript面向对象编程指南」对象
对象的属性名可加上引号,下面三行代码所定义的内容是完全相同的 var hero = { occupation : 1 }; var hero = { "occupation" : ...
- 「JavaScript面向对象编程指南」基础
DOM标准是独立的(即并不依赖JS)操作结构化文档的方式 BOM实际是个与浏览器有关的对象集合,原来没任何标准可言,H5诞生后才被定义了一些浏览器间通用的对象标准 ES5严格模式"use s ...
- 「JavaScript面向对象编程指南」原型
在 JS 中,函数本身也是一个包含了方法(如apply和call)和属性(如length和constructor)的对象,而prototype也是函数对象的一个属性 function f(){} f. ...
- 闭包初体验 -《JavaScript面向对象编程指南》
下面是我对闭包的理解:(把他们整理出来,整理的过程也是在梳理) 参考<JavaScript面向对象编程指南> 1.首先,在理解闭包之前: 我们首先应该清楚下作用域和作用域链 作用域:每个函 ...
- 《JavaScript面向对象编程指南(第2版)》读书笔记(一)
目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...
- 《JavaScript面向对象编程指南(第2版)》读书笔记(二)
<JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...
- 《JavaScript面向对象编程指南》读书笔记②
概述 <JavaScript面向对象编程指南>读书笔记① 这里只记录一下我看JavaScript面向对象编程指南记录下的一些东西.那些简单的知识我没有记录,我只记录几个容易遗漏的或者精彩的 ...
- 《JavaScript面向对象编程指南》读书笔记①
概述 JavaScript快忘完了,想看一本专业书拾遗,所以看了这本<JavaScript面向对象编程指南>. 个人觉得这本书讲的很透彻很易懂,一些原来有疑惑的地方在这本书里面豁然开朗,看 ...
- JavaScript面向对象编程指南(三) 函数
第3章 函数 3.1 什么是函数 函数:本质是一种代码的分组形式.函数的声明如下: <script type="text/javascript"> /*函数的声明组成: ...
随机推荐
- .NET 跨平台RPC框架DotNettyRPC
DotNettyRPC 1.简介 DotNettyRPC是一个基于DotNetty的跨平台RPC框架,支持.NET45以及.NET Standard2.0 2.产生背景 传统.NET开发中遇到远程调用 ...
- Web项目中的 /
如果 / 出现在路径的前面: web.xml中:http://loclalhost:8080/项目名称/ 在项目的根路径下面 jsp中: http://localhost:8080/ ...
- Charles抓包工具使用
Charles抓包工具使用和进行接口数据模拟 打开软件,选择Tools >> Map Local >> 勾选enable >> add >> ...
- Appuim的安装步骤
1.下载Appium Desktop并安装 下载地址:https://github.com/appium/appium-desktop/releases 我下载的版本为:appium-desktop- ...
- Linux(Ubuntu)使用日记------Mysql编码(utf-8)的设置
Mysq版本:5.7.21 操作系统:Linux(Ubuntu) 整个操作的基本思路如下(包括问题的解决思路,想要直接解决问题的可以先看最后的命令总结) 检查mysql编码 找到Mysql的配置文件 ...
- idea安装成功后,设置字体、快捷键、配置jdk等操作
设置字体 配置jdk 快捷键 复制当前一行: 设置自动提示,不区分大小写 关闭当前窗口 设置类头注释 自定义注释+注释快捷键 Lombok 插件安装 get.set方法报红cannot resolv ...
- C++ 中 double、 long double、long 和 long long
double 属于浮点类型,具体为双精度浮点类型,通常为 IEEE-754 64 位浮点类型. long double 也属于浮点类型,具体为扩展精度浮点类型,其精度不低于double,具体由编译器和 ...
- JPA:identifier of an instance of was altered from
由于前台提交的对象,并没有关联对象的数据. 所以要把关联对象赋值一下,在合并集合. WmsOutboundreport entity2 = service.findOne(item.getOutbou ...
- fast-ai lesson1 错误处理(CNN创建)
报错信息: name 'ConvLearner' is not defined 在最新的fast ai包中,ConvLearner已经被create_cnn取代,所以替换为下列语句就好了: learn ...
- Centos下MariaDB操作
MariaDB简介 MariaDB是mysql数据库的一个分支,操作几乎和mysql一样 MariaDB安装.启动.停止 # 安装 yum -y install mariadb mariadb-ser ...