写在前面

注:这个系列是本人对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. 打靶笔记-02-vulhub-Hackademic.RTB1

    打靶笔记-02-vulhub-Hackademic.RTB1 一.靶机信息 Name: Hackademic: RTB1(中等难度) Date release: 6 Sep 2011 Author: ...

  2. LGP3349口胡

    建议改为:如何使用FWT直接把反演题草过去 需要清楚 FWT 的本质是什么. 首先我们有一个明显的 DP: 设 \(dp[u][x][S]\) 代表 \(u\) 在图中为 \(x\),子树包含集合 \ ...

  3. python 命令运行环境下 ModuleNotFoundError: No module named 'Test'

    解决方法有两种 1. 第一种设置环境变量法 on windows the line is : SET PYTHONPATH=%cd%;%cd%\Test NOT SET PYTHONPATH=%cd% ...

  4. ASP.NET Core 6框架揭秘实例演示[25]:配置与承载环境的应用

    与服务注册一样,针对配置的设置同样可以采用三种不同的编程模式.第一种是利用WebApplicationBuilder的Host属性返回的IHostBuilder对象,它可以帮助我们设置面向宿主和应用的 ...

  5. 创世区块配置文件genesis.json的格式解读

    创世区块配置文件genesis.json的格式解读 中文网站上关于genesis 的解析大多数都来自于这个Gist:Ethereum private network configuration gui ...

  6. Visual Studio 2022 预览版下载来了(x64位)

    Visual Studio 2022 预览版下载:https://visualstudio.microsoft.com/zh-hans/vs/preview/vs2022/

  7. 突发!Gitee 图床,废了!

    大家好,我是鱼皮,这两天又发生了一件挺意外的事情:Gitee 的图床废了! 图床:指储存图片的服务器,便于在网上展示图片 昨天晚上,星球里不止一位小伙伴发帖子表示自己网站.文章中的图片竟然全部变成了 ...

  8. 学习廖雪峰的Git教程3--从远程库克隆以及分支管理

    一.远程库克隆 这个就比较简单了, git clone git@github.com:****/Cyber-security.git 远程库的地址可以在仓库里一个clone or download的绿 ...

  9. CSS3实现环形进度条?

    两个对半矩形遮罩, 使用rotate以及overflow: hidden进行旋转

  10. 服务注册和发现是什么意思?Spring Cloud 如何实现?

    当我们开始一个项目时,我们通常在属性文件中进行所有的配置.随着越来越多的服务开发和部署,添加和修改这些属性变得更加复杂.有些服务可能会下降,而某些位置可能会发生变化.手动更改属性可能会产生问题.Eur ...