写在前面

注:这个系列是本人对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. 微信退款通知,退款回调数据解密.SHA256签名AEAD_AES_256_GCM解密

    $xmlResult = file_get_contents("php://input");//获取微信的数据$result = $this->xmlToArray($xml ...

  2. 假设检验的python实现命令——Z检验、t检验、F检验

    Z检验 statsmodels.stats.weightstats.ztest() import statsmodels.stats.weightstats as sw 参数详解: x1:待检验数据集 ...

  3. 2022最新IntellJ IDEA的mall开发部署文档

    目录 版本说明 一.概述 二.基本构建 三.Git 导入编译器 四.模块描述浅析 五.配置文档 application.yml修改,涉及模块 application-dev.yml修改,涉及模块 ge ...

  4. vite2 打包的时候vendor-xxx.js文件过大的解决方法

    vite2是一个非常好用的工具,只是随着代码的增多,打包的时候 vendor-xxxxxx.js 文件也越来越大,这就郁闷了. 打包时遇到警告 输出文件名字/static/vendor.9b5698e ...

  5. 一些JDK自带的性能分析利器

    有时候碰到服务器CPU飙升或者程序卡死之类的问题,一般都不太好定位.这类bug一般都隐藏的比较深并且还可能是偶发性的,比较棘手. 对于此类问题,一般我们都有固定的分析流程.借助于JDK自带的一些分析工 ...

  6. mysql 索引模板

    DROP TABLE IF EXISTS `table_name`; CREATE TABLE `table_name` ( `id` bigint(20) UNSIGNED NOT NULL AUT ...

  7. 字节跳动社会招聘&内推-帮助你更快加入字节跳动

    字节跳动社会招聘&内推「[内推码]:4J8CA3W」 内推时间:一直有效 招聘对象:根据招聘要求而定 社招投递链接: https://job.toutiao.com/s/de5teaA 应届生 ...

  8. Python 的排序方法 sort 和 sorted 的区别

    使用 sort() 或内建函数 sorted() 对列表进行排序.它们之间的区别有两点: sort() 方法是对原列表进行操作,而 sorted() 方法会返回一个新列表,不是在原来的基础上进行操作. ...

  9. 面试问题之C++语言:说一说C++中四种cast转换

    C++中四种类型转换是:static_cast.dynamic_cast.const_cast.reinterpret_cast 1.const_cast 常量转换,用于将const变量转为非cons ...

  10. 使用 MyBatis 的 mapper 接口调用时有哪些要求?

    1.Mapper 接口方法名和 mapper.xml 中定义的每个 sql 的 id 相同: 2.Mapper 接口方法的输入参数类型和 mapper.xml 中定义的每个 sql 的 paramet ...