资讯专栏INFORMATION COLUMN

js知识梳理6:关于函数的要点梳理(2)(作用域链和闭包)

aristark / 2548人阅读

摘要:在此例中,在匿名函数被返回后,它的作用域链初始化为包含函数的活动对象和全局变量对象。函数在执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然在引用这个活动对象,结果就是只是的执行环境的作用域链会被销毁,其活动对象会留在内存中。

写在前面

注:这个系列是本人对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(value1value2){
            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.闭包的常见作用

给构造函数创建私用变量和私有函数,并定义特权方法;

创建单例,在函数最后返回公用方法

等等

总体而言,闭包对于我们理解执行环境,理解作用域链很有帮助,但平常如果不是很有必要就不要用,占用内存比较多

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/108379.html

相关文章

  • 好程序员Web前端培训入门之JS基础知识梳理汇总

    摘要:好程序员前端培训入门之基础知识梳理汇总,前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。作用域链的前端,始终是当前执行代码所在环境的变量对象。   好程序员Web前端培训入门之JS基础知识梳理汇总,Web前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。不论是专业还是非专业,有基础亦或是无基础,都想通过学习Web前端实现高薪就业。不过,学习要一...

    int64 评论0 收藏0
  • 好程序员Web前端培训入门之JS基础知识梳理汇总

    摘要:好程序员前端培训入门之基础知识梳理汇总,前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。作用域链的前端,始终是当前执行代码所在环境的变量对象。   好程序员Web前端培训入门之JS基础知识梳理汇总,Web前端工程师是当前各大企业都比较稀缺的人才,薪资待遇和就业前景都很不错。不论是专业还是非专业,有基础亦或是无基础,都想通过学习Web前端实现高薪就业。不过,学习要一...

    kviccn 评论0 收藏0
  • JS核心知识梳理——上下文、作用域、闭包、this(上)

    摘要:引言满满的干货,面试必系列,参考大量资料,并集合自己的理解以及相关的面试题,对核心知识点中的作用域闭包上下文进行了梳理。如果在小区这个作用域找到了张老师,我就会在张老师的辅导下学钢琴我张老师房间钢琴构成了学琴的上下文环境。 showImg(https://segmentfault.com/img/bVbo4hv?w=1800&h=1000); 引言 满满的干货,面试必bei系列,参考大...

    Andrman 评论0 收藏0
  • JS基础知识:变量对象、作用链和闭包

    摘要:前言这段时间一直在消化作用域链和闭包的相关知识。而作用域链则是这套规则这套规则的具体运行。是变量对象的缩写那这样放有什么好处呢我们知道作用域链保证了当前执行环境对符合访问权限的变量和函数的有序访问。 前言:这段时间一直在消化作用域链和闭包的相关知识。之前看《JS高程》和一些技术博客,对于这些概念的论述多多少少不太清楚或者不太完整,包括一些大神的技术文章。这也给我的学习上造成了一些困惑,...

    Keven 评论0 收藏0

发表评论

0条评论

aristark

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<