闭包



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面向对象编程指南」闭包的更多相关文章

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

    对象的属性名可加上引号,下面三行代码所定义的内容是完全相同的 var hero = { occupation : 1 }; var hero = { "occupation" : ...

  2. 「JavaScript面向对象编程指南」基础

    DOM标准是独立的(即并不依赖JS)操作结构化文档的方式 BOM实际是个与浏览器有关的对象集合,原来没任何标准可言,H5诞生后才被定义了一些浏览器间通用的对象标准 ES5严格模式"use s ...

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

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

  4. 闭包初体验 -《JavaScript面向对象编程指南》

    下面是我对闭包的理解:(把他们整理出来,整理的过程也是在梳理) 参考<JavaScript面向对象编程指南> 1.首先,在理解闭包之前: 我们首先应该清楚下作用域和作用域链 作用域:每个函 ...

  5. 《JavaScript面向对象编程指南(第2版)》读书笔记(一)

    目录 一.对象 1.1 获取属性值的方式 1.2 获取动态生成的属性的值 二.数组 2.1 检测是否为数组 2.2 增加数组长度导致未赋值的位置为undefined 2.3 用闭包实现简易迭代器 三. ...

  6. 《JavaScript面向对象编程指南(第2版)》读书笔记(二)

    <JavaScript面向对象编程指南(第2版)>读书笔记(一) <JavaScript面向对象编程指南(第2版)>读书笔记(二) 目录 一.基本类型 1.1 字符串 1.2 ...

  7. 《JavaScript面向对象编程指南》读书笔记②

    概述 <JavaScript面向对象编程指南>读书笔记① 这里只记录一下我看JavaScript面向对象编程指南记录下的一些东西.那些简单的知识我没有记录,我只记录几个容易遗漏的或者精彩的 ...

  8. 《JavaScript面向对象编程指南》读书笔记①

    概述 JavaScript快忘完了,想看一本专业书拾遗,所以看了这本<JavaScript面向对象编程指南>. 个人觉得这本书讲的很透彻很易懂,一些原来有疑惑的地方在这本书里面豁然开朗,看 ...

  9. JavaScript面向对象编程指南(三) 函数

    第3章 函数 3.1 什么是函数 函数:本质是一种代码的分组形式.函数的声明如下: <script type="text/javascript"> /*函数的声明组成: ...

随机推荐

  1. linux下安装多个Tomcat

    编辑环境变量:vi /etc/profile 加入以下代码 ##########first tomcat########### CATALINA_BASE=/usr/local/src/tomcat ...

  2. HTML5网页点击分享到whatsapp

    一.在网页头部加入分享标题和url,代码如下: <meta name="whatsapp:url" class="share_url" content=& ...

  3. OpenStack之Fuel架构及其工作原理

      一.Fuel架构 Fuel是一个开源的OpenStack部署和管理的工具,它允许用户通过Web UI进行OpenStack的部署和管理,并可以通过插件来扩展. Fuel的目标:加快和简化不同配置的 ...

  4. 在oracle表中增加字段,并调整字段的顺序

    增加字段的语句很简单,以用户身份连接oracle服务: alter table tablename add(colname coltype); # 填上表名.字段名.字段类型 修改字段顺序前,查看表中 ...

  5. Asp.net mvc 动作方法中 调用 另一个控制器的动作方法

    public ActionResult 动作方法(){ var otherController = DependencyResolver.Current.GetService<另一个控制器的类名 ...

  6. 解决使用Spring Boot、Multipartfile实现上传提示无法找到文件的问题

    前言 SpringBoot使用MultiPartFile接收来自表单的file文件,然后进行服务器的上传是一个项目最基本的需求,我以前的项目都是基于SpringMVC框架搭建的,所以在使用Spring ...

  7. kettle变量(param命名参数2)

    接arg参数: 通过命令行进行变量赋值和引用 定义跟界面定义相同: 赋值(转换): 运行命令到kettle目录下 pan /file:path "/param:aa="bb&quo ...

  8. MySQL中怎么对varchar类型排序问题(转)

    在mysql默认order by 只对数字与日期类型可以排序,但对于varchar字符型类型排序好像没有用了,下面我来给各位同学介绍varchar类型排序问题如何解决.   今天在对国家电话号码表进行 ...

  9. JS 时间字符串与时间戳之间的转换

    1.当前时间换时间戳 var timestamp = parseInt(new Date().getTime()/1000); // 当前时间戳 document.write(timestamp); ...

  10. Nginx 过滤sub模块

    L70 通过 --with-http_sub_module 编译进nginx sub_filter 指令 Syntax: sub_filter string replacement; Default: ...