用垃圾回收机制解释JavaScript中的闭包

栏目: JavaScript · 发布时间: 5年前

内容简介:说起javascript中的闭包,首先要知道为什么会存在闭包,其作用又是什么。且为什么闭包中就能让外层函数的变量始终保存呢?下面我们将从这两个角度去剖析它。当然,大神绕道,谢谢哈。开门见山,直接总结闭包的两大核心作用:众所周知,变量在javascript中有全局变量和局部变量,函数内部可以访问外部的全局变量,而外部无法访问函数内部的局部变量,这是JS的一个特点:

说起javascript中的闭包,首先要知道为什么会存在闭包,其作用又是什么。且为什么闭包中就能让外层函数的变量始终保存呢?下面我们将从这两个角度去剖析它。当然,大神绕道,谢谢哈。

开门见山,直接总结闭包的两大核心作用:

  1. 读取函数内部的变量;
  2. 让变量始终保存在内存中。

一、读取函数内部的变量

众所周知,变量在javascript中有全局变量和局部变量,函数内部可以访问外部的全局变量,而外部无法访问函数内部的局部变量,这是JS的一个特点:

var n = 999;
function f1() {
    console.log(n) 
}
f1(); // 999 
复制代码

那么摆在我们面前的一个问题就是:如何在函数外部访问到函数内部的变量?有一种办法就是在函数内部再定义一个函数,通过这个内层函数去访问外层函数的变量,因为内层函数同属外层函数的作用域中(符合链式作用域结构规则,子对象可以一级一级地向上寻找所有父对象的变量):

funtion f1() {
    var n = 999;
    function f2() {
        console.log(n);
    }
    return f2;
}

var result = f1();
result(); // 999
复制代码

对此:阮一峰老师给出了一个通俗的关于闭包的解释:闭包就是能够读取其他函数内部变量的函数(关于到底是内层函数是闭包还是外层函数是闭包的解释各方不一致,但这不是重点)。

由于在JS中只有函数内部的子函数才能读取局部变量,因此也可以理解为定义在一个函数内部的函数。

二、让变量始终保存在内存中

暂且我们说闭包是指有权访问另一个函数作用域中的变量的函数吧,函数内部的函数使用到外层函数的变量,使得外层函数的变量的生存时间延长,造成常驻内存。

function foo(){
    var a = 2;
    return function(){
      a += 1;
      console.log(a);
   }
}

var baz = foo();

baz(); // 3
baz(); // 4
baz(); // 5
baz(); // 6
复制代码

上面的例子为什么每次调用baz时,a都没有被初始化赋值呢? 接下来就要从JS的垃圾回收机制去考虑了。

1. 垃圾回收机制

通常我们可以使用引用计数法判断代码中的变量是否被释放,即语言引擎中有一张“引用表”,保存了内存里面所有的资源(通常是各种值)的引用次数,如果一个值的引用次数为0,则表示该值不再用到了,因此改值也被内存释放了。比如:

cont arr = [1,2,3];
// 数组[1,2,3]是一个值,会占用内存变量arr是仅有的对这个数组的引用,因此引用次数为1。尽管只赋值一次,后面代码中再没有调用,但却依然占据内存.
复制代码

譬如JS这样的高级程序语言中都嵌入了一种称为垃圾回收器的机制,其工作是跟踪内存的分配和使用,以便发现任何时候不再需要的内存并对其进行释放。举例如下:

var o1 = {
    o2: {
        x: 1
    }
};
//创建2个对象o2和o1,其中o2被o1对象引用作为其属性,此时没有垃圾可收集

var o3 = o1; //创建变量o3,引用由o1指向的对象的变量
o1 = 1 ; //现在将o1重新赋值为1,最初的o1中的对象由o3变量表示
var o4 = o3.o2; //创建变量o4,引用对象o2,此时o2被两个地方引用:一个是作为o3变量的属性,一个是作为o4变量
o3 = '666'; // 此时最初o1对象应没有再被引用了,可以被垃圾收集了,但是最初的o2还在被o4引用,因此还不能被垃圾收集

o4 = 16; //此时 o2也可以说再见了...
复制代码

2. 解释内存保留

好了,现在再回过头来看看上面那个闭包题,用垃圾回收的思想来作出解答:

function foo(){
    var a = 2;
    function outer() {
        a += 1;
	console.log(a);
    }
    return outer
}

var baz = foo(); //在全局作用域下创建变量baz

复制代码

此时,baz指向的就是 outer,所以outer始终都没有被销毁,而根据垃圾回收机制,由于在outer中有引用外层函数的变量a,因此a也一直没有被销毁,所以就出现这种现象:

baz(); //3
baz(); //4
baz(); //5
复制代码

是不是以下就明白了,接下来看一个简单的经典例题

三、经典例题

看一下经典的面试题吧,用垃圾回收的思想来思想一下结果,就会很顺利:

var callbacks;
for(var i = 0 ; i <= 5 ;i ++) {
   callbacks = function() {
      console.log(i);
   }
}
callbacks(); //6
callbacks(); //6
callbacks(); //6
复制代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

HTML5

HTML5

Matthew David / Focal Press / 2010-07-29 / USD 39.95

Implement the powerful new multimedia and interactive capabilities offered by HTML5, including style control tools, illustration tools, video, audio, and rich media solutions. Understand how HTML5 is ......一起来看看 《HTML5》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具