JavaScript闭包(closures)初探

本篇博客记录JavaScript中闭包的使用,这也是在面试过程中被问到的点。

概念

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

首先我们要明白闭包是什么: **闭包是指有权访问另一个函数作用域中的函数。**创建闭包的常见方式为在一个函数内部创建另一个函数,我们以以下代码举例。

function createComparisonFunction(propertyName) {
    return function (obj1, obj2) {
        var value1 = obj1[propertyName];
        var value2 = obj2[propertyName];
        
        if (value1 > value2) {
            return 1;
        } else if(value1 < value2) {
            return -1;
        } else {
            return 0;
        }
    }
}

从代码中可以看出,内部匿名函数可以访问外部函数的 propertyName 属性,这是因为内部函数的作用域链中包含了外部函数的作用域,想要这地理解闭包就必须从先理解作用域链,可以参考我往期的博客,这里再做一下简单介绍。

作用域链

函数调用的详细介绍

  • 创建函数时,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在函数内部的 [[Scope]]
  • 当某个函数被调用时,会创建一个执行环境(execution context)
  • 复制函数中的 [[Scope]] 属性创建作用域链
  • 创建一个新的活动对象并被推入作用域链前端
  • 使用 arguments 和其他命名参数来初始化函数的活动对象(activation object)

无论什么时候在函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量,一般当函数执行完毕后,局部活动对象就会被销毁,内存中仅保存全局作用域。

几个代码实例

var name = "The Window";

var object = {
    name : "My Object",

    getNameFunc : function(){
        return function(){
            return this.name;
        };
    }
};

alert(object.getNameFunc()());  //"The Window"
var name = "The Window";
            
var object = {
    name : "My Object",

    getNameFunc : function(){
        var that = this;
        return function(){
            return that.name;
        };
    }
};

alert(object.getNameFunc()());  //"MyObject"

对比以上两个代码的结果可知,闭包函数被调用时,会自动搜索两个对象 thisarguments, 二这两个变量只会搜索到活动对象位置, 在这里我们可以理解为第一个代码中闭包在搜索 this时 首先搜索闭包函数内部有没有 name, 然后再搜索 getNameFunc 中有没有 name, 最后在全局变量中搜索 name, 所以最终打印 The window

执行环境与活动对象的区别

var与let在闭包中的体现

看下面几段代码:

function createFuncitions() {
    var result = new Array();

    for (let i=0; i < 10; i++) {
        result[i] = function () {
            return i;
        };
    }

    return result;
}
function createFuncitions() {
    var result = new Array();

    for (var i=0; i < 10; i++) {
        result[i] = function () {
            return i;
        };
    }

    return result;
}

当我们分别调用 createFuncitions 返回的函数数组时,发现循环中使用 let 时,调用结果为0到9, 但是循环中使用 var 时, 调用结果都为10。这是因为闭包只能取得包含函数中任何变量中的最后一个值,当使用 var时,每个函数都引用着保存变量 i的同一个变量对象。

风险

闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存,过度使用闭包可能会导致内存占用过多。
可用调用匿名函数的方法来减少闭包内存占用的问题,因为没有指向匿名函数的引用,只要函数执行完毕,就可以立即销毁其作用域了。

(function () {
}) ()

参考

Closure

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值