JavaScript 中的闭包和作用域链(读书笔记)

  要想理解闭包,应当先理解JavaScript的作用域和作用域链。

  JavaScript有一个特性被称之为“声明提前(hoisting)”,即JavaScript函数里声明的所有变量(但不涉及赋值)都被“提前”至函数体的顶部,“声明提前”这步操作是在JavaScript引擎的“预编译”时进行的,是在代码开始运行之前,看一下下面的例子:

var name = "YY";
function getName(){console.log(name);      //输出undefine,而不是“YY”var name = "Crucify";console.log(name);      //输出“Crucify”
}

  首先局部变量定义了一个和全局变量相同名字的变量,则在函数体内部局部变量遮盖了同名的全局变量,然后在函数体内部变量name的声明被提前至函数体顶部但并没有赋值,所以此时name是一个只被声明但并没有初始化的变量,我们知道变量只进行声明但并不初始化则它的值为undefine,所以第一行打印时undefine,下一行开始为变量name进行赋值,所以第二行打印的输出是我们所期望的。

 

  当某个函数被调用时,会创建一个执行环境及相应的作用域链。

  执行环境(execution context)定义了变量或函数有权访问的其他数据,决定了它们各自的行为。每个函数都有自己的执行环境。当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。当代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain)。作用域链的用途,是保证对执行环境有权访问的所有变量和函数的有序访问。

  作用域链本质上是一个指向变量对象的指针列表,它只引用但不实际包含变量对象。当JavaScript需要查找变量x的值的时候(这个过程称作“变量解析”(variable resolution)),他会从列表之中的第一个对象开始查找直到最后一个对象,如果某个对象有一个名为x的属性,则会直接食用这个属性的值。如果作用域链上没有任何一个对象含有属性x,那么就认为这段代码的作用域上不存在x,并最终抛出一个引用错误异常。

  所谓的“变量对象的指针列表”很好理解。在JavaScript的最顶层代码中(也就是不包含在任何函数定义内的代码),作用域链是由一个全局对象组成。在不包含嵌套的函数体内,作用域链上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。在一个嵌套的函数体内,作用域链上则至少有三个对象,看下面的例子:

var name = "YY";
function getName(){var name = "Crucify";function f(){return name;          }return f();
}

  函数f()的作用域链上有三个对象,第一个是定义函数f()参数和局部变量的对象,第二个是定义函数getName()参数和局部变量的对象,第三个是全局对象。

  每个环境都可以向上搜索作用域链,以查询变量和函数名,但任何环境都不能通过向下搜索作用域链而进入另一个执行环境,即函数f()可以向上搜索函数getName()和全局对象中的属性,但是全局对象不能向下搜索getName()和f()中的值。

 

  而创建闭包的常见方式,就是在一个函数内部创建另一个函数。闭包是指有权访问另一个函数作用域中的变量的函数。

  一般来讲,当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域(全局执行环境的变量对象)。但是,闭包的情况有所不同,因为在另一个函数内部定义的函数会将包含函数(即外部函数)的活动对象添加到它的作用域链中,参考下面的代码:

function createComparisonFunction(propertyName) {return function(object1, object2){var value1 = object1[propertyName];var value2 = object2[propertyName];if (value1 < value2){return -1;} else if (value1 > value2){return 1;} else {return 0;}};
}

当下列代码执行时,包含函数与内部匿名函数的作用域链如图所示:

var compare = createComparisonFunction("name");
var result = compare({ name: "Nicholas" }, { name: "Greg" });

 

  当createComparisonFunction()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象。直到匿名函数被销毁后, createComparisonFunction()的活动对象才会被销毁:

compare = null;  //解除对匿名函数的引用(以便释放内存)

  由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多,所以在绝对必要时再考虑使用闭包。

  作用域链的这种配置机制引出了一个值得注意的副作用,即闭包只能取得包含函数中任何变量的最后一个值。因为闭包所保存的是整个变量对象,而不是某个特殊的变量:

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

  这个函数会返回一个函数数组,且每个函数都返回 10。

  在闭包中使用 this 对象也可能会导致一些问题。我们知道, this 对象是在运行时基于函数的执行环境绑定的,而匿名函数的执行环境具有全局性,因此其 this 对象通常指向 window(在通过 call()或 apply()改变函数执行环境的情况下, this 就会指向其他对象),看下面的例子:

var name = "The Window";
var object = {name : "My Object",getNameFunc : function(){return function(){return this.name;};}
};
alert(object.getNameFunc()()); //"The Window"(在非严格模式下)

  把外部作用域中的 this 对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了,如下所示:

var name = "The Window";
var object = {name : "My Object",getNameFunc : function(){var that = this;return function(){return that.name;};}
};
alert(object.getNameFunc()()); //"My Object"

arguments 也存在同样的问题。如果想访问作用域中的 arguments 对象,必须将对该对象的引用保存到另一个闭包能够访问的变量中。

转载于:https://www.cnblogs.com/crucify-lee/p/4521273.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/257264.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

韩师师范学院计算机科学与技术在哪个学区,2017年韩山师范学院本科插班生考试《数据结构》A卷...

韩山师范学院2017年本科插班生考试试卷计算机科学与技术 专业 数据结构 试卷(A 卷)一、单项选择题(每题2分&#xff0c;共30分)1. 对线性表&#xff0c;在下列哪种情况下应当采用链表表示&#xff1f;( ) A. 经常需要随机地存取元素 B. 经常需要进行插入和删除操作 C. 表中元素…

JAVA取随机数,石头剪刀布实例

一、取随机数&#xff1a; import java.util.Random; //导入随机数 public class Test{public static void main(String[] args){Random xx new Random(); //声明随机数int number xx.nextInt(10); //赋值随机数给numberSystem.out.println("随机数…

第一阶段

初步实现了相机的调用&#xff0c;做了简单界面&#xff0c;并没有实现核心功能 Button button (Button) findViewById(R.id.sao);button.setOnClickListener(new OnClickListener(){Overridepublic void onClick(View v) {Intent intent new Intent(MediaStore.ACTION_IMAGE…

JavaScript 详说事件机制之冒泡、捕获、传播、委托

DOM事件流&#xff08;event flow &#xff09;存在三个阶段&#xff1a;事件捕获阶段、处于目标阶段、事件冒泡阶段。 事件捕获&#xff08;event capturing&#xff09;&#xff1a;通俗的理解就是&#xff0c;当鼠标点击或者触发dom事件时&#xff0c;浏览器会从根节点开始…

很棒的HTML5效果实例

2019独角兽企业重金招聘Python工程师标准>>> http://mrdoob.com/141/Internet_Explorer_with_WebGL 转载于:https://my.oschina.net/u/3647620/blog/1552495

计算机一级网络操作题没点回答,计算机等级一级考试操作题1(附答案)

一、选择题1、在计算机领域中通常用mips来描述______。a、计算机的运算速度 b、计算机的可靠性 c、计算机的可运行性 d、计算机的可扩充性2、微型计算机存储系统中&#xff0c;prom是______。a、可读写存储器 b、动态随机存取存储器 c、只读存储器 d、可编程只读存储器3、按161…

分配的访问权限的展台应用:最佳做法

原文: 分配的访问权限的展台应用&#xff1a;最佳做法 best practices guidance for developing a kiosk app for assigned access. 在 Windows 10 中&#xff0c;你可以使用锁屏框架和分配的访问权限创建展台应用&#xff0c;该应用允许用户与设备上的单个应用进行交互。 本文…

SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问的解决方案...

今天写了一个excel表的导入功能&#xff0c;结果在excel表中的内容导入到页面时报错&#xff1a;SQL Server 阻止了对组件 Ad Hoc Distributed Queries 的 STATEMENTOpenRowset/OpenDatasource 的访问&#xff0c;因为此组件已作为此服务器安全配置的一部分而被关闭。系统管…

Mongo客户端MongoVUE的基本使用

这里没有涉及到服务器以及客户端的安装&#xff0c;文章主要介绍mongo客户端mongoVUE的使用 一、数据库连接 点击绿色加号添加一个连接&#xff0c;输入name、server、port&#xff0c;点击save&#xff0c;点击connect进行连接 二、添加 1.右键添加一个Database 2.输入名称&am…

Vim杂记:Sublime的配色方案

一、前言                                     爱美之心人皆有之&#xff0c;sublime的配色实在好看&#xff0c;于是希望Vim也能这样。 二、配置                                     1.下载monok…

计算机一级考试有三科,全国计算机一级考试是一级WPS Office 一级MS Office 一级Photoshop 三个任选一个考试吗?...

满意答案nanrrui3j2017.08.24采纳率&#xff1a;41% 等级&#xff1a;9已帮助&#xff1a;415人全国计算机一级考试是有考试大纲的&#xff0c;按照大纲要求是三科都考。一级MS Office、一级WPS Office、一级Photoshop&#xff0c;一级共三个科目。完全采取上机考试形式&…

Docker学习(三):镜像

2019独角兽企业重金招聘Python工程师标准>>> 1、简介 docker运行前需要本地存在对应的镜像&#xff0c;若镜像不存在本地&#xff0c;docker会先尝试从默认的镜像仓库下载&#xff08;Docker Hub公共注册服务器中的仓库&#xff09;。用户也可以配置&#xff0c;使用…

系统流程图

转载于:https://www.cnblogs.com/ADCARRY/p/5462270.html

一年级下册计算机教学计划,【实用】一年级下册教学计划4篇

【实用】一年级下册教学计划4篇光阴迅速&#xff0c;一眨眼就过去了&#xff0c;我们的教学工作又将抒写新的篇章&#xff0c;写好教学计划才不会让我们努力的时候迷失方向哦。相信大家又在为写教学计划犯愁了吧&#xff0c;下面是小编精心整理的一年级下册教学计划4篇&#xf…

PowerDesigner中NAME和COMMENT的互相转换,需要执行语句

原文&#xff1a;http://www.cnblogs.com/yelaiju/archive/2013/04/26/3044828.html由于PDM 的表中 Name 会默认Code 所以很不方便, 所以需要将 StereoType 显示到表的外面来 打开[工具]->[显示属性](英文:Display Preferences) ->Content->Table->右边面板Columns…

成都计算机中心起名,成都给宝宝起名的地方哪里好

成都给宝宝起名的地方哪里好2018-09-21每一个人都拥有一个好听大气的名字&#xff0c;名字作为一个人的标识&#xff0c;是人与人交流的代号&#xff0c;因此有一个好听寓意大气的名字是很关键的&#xff0c;名字毕竟关乎着对他人的第一印象&#xff0c;所以在起名字方面父母们…

20150206--JS巩固与加强4-02

20150206--JS巩固与加强4-02 五、属性遍历与删除 1、使用for…in…遍历自定义对象属性 基本语法&#xff1a; for…in…&#xff08;主要是完成对对象的遍历&#xff09; 示例代码: 运行效果&#xff1a; 说明&#xff1a;在17行代码&#xff0c;不能使用p1.i形式进行自定义对象…

面向对象课程 - T-shirt

拿到了一件谜一样的T-shirt 吓得我赶紧捏了下hbb 转载于:https://www.cnblogs.com/andwho/p/5465165.html

状态开关(ToggleButton)

状态开关&#xff08;ToggleButton&#xff09;&#xff1a; 常用属性&#xff1a;isChecked&#xff08;是否被选中&#xff0c;如true&#xff09; 监听&#xff1a;1.监听方法&#xff1a;setOnCheckedChangeListener 2.监听器&#xff1a;CompoundButton.OnCheckedChangeLi…

冲刺第二阶段

转载于:https://www.cnblogs.com/lixin20/p/4549338.html