写在前面

注:这个系列是本人对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)(作用域链和闭包)的更多相关文章

  1. 前端高质量知识(四)-JS详细图解作用域链与闭包

    攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你是初入前端的朋友,我没有办法 ...

  2. JS详细图解作用域链与闭包

    JS详细图解作用域链与闭包 攻克闭包难题 初学JavaScript的时候,我在学习闭包上,走了很多弯路.而这次重新回过头来对基础知识进行梳理,要讲清楚闭包,也是一个非常大的挑战. 闭包有多重要?如果你 ...

  3. js深入(三)作用域链与闭包

    在之前我们根绝对象的原型说过了js的原型链,那么同样的js 万物皆对象,函数也同样存在这么一个链式的关系,就是函数的作用域链 作用域链 首先先来回顾一下之前讲到的原型链的寻找机制,就是实例会先从本身开 ...

  4. javascript中函数的执行环境、作用域链、变量对象与活动对象

    javascript高级程序设计中:对执行环境.作用域链.变量对象.活动对象的解释: 1.执行环境: 执行环境:有时也叫环境:是JavaScript中最为重要的一个概念:执行环境定义了变量或函数有权访 ...

  5. js之作用域链到闭包

    一.作用域 全局作用域和函数作用域(局部作用域). 一个变量的作用域就是源代码中定义这个变量的区域. 二.作用域链和闭包 全局变量只有一个(window,globel),全局环境下每一个函数都会形成一 ...

  6. 1--面试总结-js深入理解,对象,原型链,构造函数,执行上下文堆栈,执行上下文,变量对象,活动对象,作用域链,闭包,This

    参考一手资料:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/中文翻译版本:https://zhuanlan.zhihu.com/p ...

  7. js知识梳理5:关于函数的要点梳理(1)

    写在前面 注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者.有发现什么问题的,欢迎留言指出 ...

  8. 深入理解JS函数作用域链与闭包问题

    function fun(n,o) { console.log(o) return { fun:function(m){ return fun(m,n); } }; } ); a.fun(); a.f ...

  9. js隐式类型转换,预编译、递归、作用域,作用域链、闭包、立即执行函数、继承圣杯模式

    隐式类型转换 调用Number()当有运算符(加减乘除,求余)时,会调用Number()转为数字再运算,除了 加 当 有字符串时就变身成拼接Boolean();String(); typeof()st ...

随机推荐

  1. 微信小程序下滑时能实现加载更多数据

    wxml代码: <view class="scroll"> <!-- 绑订页面上拉触底事件的处理函数onReachBottom事件 --> <scro ...

  2. php 23种设计模型 - 工厂模式

    工厂模式(Factory) 工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建 ...

  3. VMWare ESXi 6.0如何复制虚拟机

    1.复制前建议将待复制的虚拟机电源关闭. 2.登录ESXi 6.0主机后,点击"配置"选项卡,再点击"存储器". 3.能看得相关的数据存储,然后右键点击存储器如 ...

  4. JDK ThreadLocal解析

    Java ThreadLocal解析 ThreadLocal 线程本地变量, 线程私有, 在 Thread 类中用 ThreadLocal.ThreadLocalMap threadLocals 以数 ...

  5. CF908G&LOJ6697口胡

    为什么我从ACAM做到了数位DP啊 考虑枚举前缀顶着最高位和后缀没有顶着的最高位. 考虑计算一个数对答案的贡献.统计 \(t\) 的出现次数记录到 \(c[t]\) 中. 贡献就是 \(\sum_{i ...

  6. LGP7847题解

    题意:给定 \(n\),求方程 \(\frac 1 a - \frac 1 b=\frac 1 n\) 的所有解,且解必须满足 \(\gcd(a,b,n)=1\). 以下内容搬运自官方题解: 转化一下 ...

  7. Django基础七之CBV装饰器和中间件

    Django基础七之CBV装饰器和中间件 目录 Django基础七之CBV装饰器和中间件 1. CBV加装饰器 2. Django中间件 2.1 Django中间件介绍 2.2 自定义中间件 2.2. ...

  8. mybatis连接sql

    mysql6以上  com.mysql.cj.jdbc.Driver

  9. Mybatis将mapper映射文件配置到recources下

    关于为什么要将Mybatis的mappers.xml文件配置到resources目录下的粗浅看法: (1).使文件目录更加清晰.resources文件目录下通常为配置文件,所以将Mappers.xml ...

  10. IDEA端口占用的解决方案

    使用端口查找到对应的进程PID: netstat -ano | findstr "端口" 查找到对应PID的进程信息 tasklist /v /fi "PID eq &l ...