js知识梳理6:关于函数的要点梳理(2)(作用域链和闭包)
写在前面
注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者。有发现什么问题的,欢迎留言指出。
1.执行环境
- 执行环境简称“环境”,定义了变量或函数有权访问的其他数据。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。
- 全局执行环境是最外围的一个执行环境。在Web浏览器中,全局执行环境被认为是window对象,因此所有全局变量和函数都是作为window对象的属性和方法创建的。
- 某个执行环境中的所有代码执行完毕,环境被销毁,保存在其中的所有变量和函数定义也随之销毁。
- 执行流:每个函数都有自己的执行环境,当执行流进入一个函数时,函数的环境就会被推入一个环境栈中。而在函数执行之后,栈将其环境弹出,把控制权返回给之前的执行环境。
- 代码在一个环境中执行时,会创建变量对象的一个作用域链(scope chain),用途是保证对执行环境有权访问的所有变量和函数的有序访问。作用域链的前端,始终都是当前执行的代码所在环境的变量对象。如果这个环境是函数,则将其活动对象作为变量对象。活动对象在最开始时只包含arguments 对象。作用域链的下一个变量对象来自包含环境,而再下一个变量对象则来自下一个包含环境。这样,一直延续到全局执行环境;全局执行环境的变量对象始终都是作用域链中的最后一个对象:
var color = "blue";
function changeColor() {
if(color == "blue"){
color = "red";
}else{
color = "blue";
}
}
changeColor();
console.log("color is:" + color);//red
例子中,函数changeColor()的作用域链包含两个对象:它自己的变量对象(其中定义着arguments对象)和全局环境的变量对象。可以在函数内部访问变量 color,就是因为可以在作用域链中找到它。
var color = "blue";
function changeColor() {
var anotherColor = "red";
function swapColors() {
var tempColor = anotherColor;
anotherColor = color;
color = tempColor;
//这里可以访问color、anotherColor和tempColor
}
//这里可以访问color和anotherColor,但不能访问tempColor
swapColors();
}
// 这里只能访问color
changeColor();
以上共涉及3个执行环境:全局环境、changeColor()的局部环境和swapColors()的局部环境。显然,内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。
2.闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数。
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 compareNames = createComparisonFunction("name");
//调用函数
var result = compareNames({name:'jaychou'},{name:'xiaoming'});
//解除对匿名函数的引用(释放内存)
compareNames = null;
以上过程:
1.定义函数内部的函数时,会将它的包含函数的活动对象添加到它的作用域链中。在此例中,在匿名函数被返回后,它的作用域链初始化为包含createComparisonFunction()函数的活动对象和全局变量对象。
2.createComparisonFunction()函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象,结果就是只是createComparisonFunction()的执行环境的作用域链会被销毁,其活动对象会留在内存中。
3.直到代码中将匿名函数置为null释放内存后,createComparisonFunction()的活动对象才会被销毁。
注意:由于闭包会携带包含它的函数的作用域,所以会比其他函数占用更多的内存,过多使用闭包会导致内存占用过多,所以在很有必要时才考虑使用闭包。
3.闭包的最常见问题
function createFunctions() {
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function () {
return i;
};
}
return result;
}
console.log(createFunctions()[4]());//会打印10
数组里面的每个函数都是打印10,因为每个函数的作用域链中都保存着createFunctions()函数的活动对象,所以他们引用的都是同一个变量i。当函数数组被返回时,变量i的值是10,所以就是上面的结果了。通过创建另一个匿名函数的改造如下符合预期:
function createFunctions() {
var result = new Array();
for(var i=0;i<10;i++){
result[i] = function (num) {
return function () {
return num;
}
}(i);
}
return result;
}
console.log(createFunctions()[4]());//会打印4
没有直接把闭包赋值给数组,而是定义了一个匿名函数,并将立即执行该匿名函数的结果赋给函数,函数参数是按值传递的,所以会把变量i的当前值复制给参数num。
在这个立即执行函数里面创建并返回了一个访问num的闭包。这个闭包被返回时都准确包含了对应的包含环境的活动对象的num值。
4.闭包的常见作用
- 给构造函数创建私用变量和私有函数,并定义特权方法;
- 创建单例,在函数最后返回公用方法
等等
总体而言,闭包对于我们理解执行环境,理解作用域链很有帮助,但平常如果不是很有必要就不要用,占用内存比较多。
js知识梳理6:关于函数的要点梳理(2)(作用域链和闭包)的更多相关文章
- 前端高质量知识(四)-JS详细图解作用域链与闭包
攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你是初入前端的朋友,我没有办法 ...
- JS详细图解作用域链与闭包
JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你 ...
- js深入(三)作用域链与闭包
在之前我们根绝对象的原型说过了js的原型链,那么同样的js 万物皆对象,函数也同样存在这么一个链式的关系,就是函数的作用域链 作用域链 首先先来回顾一下之前讲到的原型链的寻找机制,就是实例会先从本身开 ...
- javascript中函数的执行环境、作用域链、变量对象与活动对象
javascript高级程序设计中:对执行环境.作用域链.变量对象.活动对象的解释: 1.执行环境: 执行环境:有时也叫环境:是JavaScript中最为重要的一个概念:执行环境定义了变量或函数有权访 ...
- js之作用域链到闭包
一.作用域 全局作用域和函数作用域(局部作用域). 一个变量的作用域就是源代码中定义这个变量的区域. 二.作用域链和闭包 全局变量只有一个(window,globel),全局环境下每一个函数都会形成一 ...
- 1--面试总结-js深入理解,对象,原型链,构造函数,执行上下文堆栈,执行上下文,变量对象,活动对象,作用域链,闭包,This
参考一手资料:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/中文翻译版本:https://zhuanlan.zhihu.com/p ...
- js知识梳理5:关于函数的要点梳理(1)
写在前面 注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者.有发现什么问题的,欢迎留言指出 ...
- 深入理解JS函数作用域链与闭包问题
function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } ); a.fun(); a.f ...
- js隐式类型转换,预编译、递归、作用域,作用域链、闭包、立即执行函数、继承圣杯模式
隐式类型转换 调用Number()当有运算符(加减乘除,求余)时,会调用Number()转为数字再运算,除了 加 当 有字符串时就变身成拼接Boolean();String(); typeof()st ...
随机推荐
- 微服务7:通信之RPC
★微服务系列 微服务1:微服务及其演进史 微服务2:微服务全景架构 微服务3:微服务拆分策略 微服务4:服务注册与发现 微服务5:服务注册与发现(实践篇) 微服务6:通信之网关 微服务7:通信之RPC ...
- Kubernetes:Ingress总结(一)
Blog:博客园 个人 参考:Ingress | Kubernetes.<Kubernetes进阶实战>.<Kubernetes网络权威指南 > 何谓Ingress?从字面意思 ...
- LGP3709题解
题目大意 简化后为区间众数出现次数,简化前为[数据删除] 吐槽 为什么题解只有一篇分块,剩下的全是莫队? 这题不是蒲公英?这和算导例题有何区别??? 为什么现在的人都喜欢去看题解而不注重思维??? 莫 ...
- LGP3346题解
广义 SAM 比较简单的题/fad 题意:树上所有路径一共能够组成多少个本质不同子串? 并且数据保证最多只有20个叶子节点. 我们先来考虑一下一种特殊情况: 对于路径 \([u,v]\),\(u\) ...
- python练习册 每天一个小程序 第0002题
1 #-*-coding:utf-8-*- 2 __author__ = 'Deen' 3 ''' 4 题目描述: 5 将 0001 题生成的 200 个激活码(或者优惠券)保存到 MySQL 关系型 ...
- 如何在 Java 中实现二叉搜索树
二叉搜索树 二叉搜索树结合了无序链表插入便捷和有序数组二分查找快速的特点,较为高效地实现了有序符号表.下图显示了二叉搜索树的结构特点(图片来自<算法第四版>): 可以看到每个父节点下都可以 ...
- Unity-2D
Unity-2D 1.Unity中的2D模式: 1)游戏在二维上展示 启用 2D 模式时将会设置正交(即无透视)视图:摄像机沿 Z 轴观察,而 Y 轴向上增加.因此可以轻松可视化场景并放置 2D 对象 ...
- Mybatis配置错误:java.lang.ExceptionInInitializerError
情况一:配置文件,无法被导出或者生效 修改前: 修改后: 究其原因,这是由于Maven的约定大于配置,导致我们写的配置文件,无法被导出或者生效的问题,解决方案: 在pom.xml文件中配置导出非res ...
- B+树叶子节点数据如何存储,以及如何查找某一条数据
MySQL索引背后的数据结构及算法原理 https://www.kancloud.cn/kancloud/theory-of-mysql-index 非常好 根据一条sql 如何查看索引结构等信息 ...
- 请写出你最常见到的5个runtime exception?
对于一个有1-2年左右编程经验的人来说,总会经常遇到一些常见的异常,其中有些就是Runtime Exception.比如: NullPointerException - 当调用一个未初始化的引用变量( ...