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.闭包的罕见作用

  • 给组织函数建立私用变量和私有函数,并定义特权要领;
  • 建立单例,在函数末了返回公用要领

等等

整体而言,闭包关于我们明白实行环境,明白作用域链很有协助,但寻常假如不是很有必要就不要用,占用内存比较多

    原文作者:QCJay
    原文地址: https://segmentfault.com/a/1190000016082884
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞