最近正逢过十一,有了大块的时间,可以给自己充充电。于是便开始了《你不知道的JavaScript 上卷》之旅。最开始的几章描述的是JS的相关编译原理,作用域,以及声明提升的相关知识。这些内容虽然很重要,但是不是本文的重点。本文的重点是作用域的闭包,为什么呢?因为到现在为止,对这个概念还是云里雾里,所以在这里做下记录。
闭包的定义
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数在当前词法作用域之外执行。
这么说,是不是还是有点不懂。那么我们可以换个说法:无论通过何种方法将内部函数传递到词法作用域之外,他都会持有对原始定义作用域的引用,无论在何处执行这个函数,都会使用闭包。
这么讲还不是很懂?没关系,那么我们可以看一下菜鸟教程对闭包的讲解:
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。直观的说就是形成一个不销毁的栈环境。
还是不太懂? 没关系,让我们来看一下mozilla的定义:
闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量
因为原生的JavaScript是不支持私有变量的,所以闭包相当于对于私有的变量的一种实现。如果没有私有变量的话,那么我们的代码很有可能会出现很多稀奇古怪的问题。熟悉面向对象语言的人(例如 Java),一定知道要把实体类中的属性定义为private的。原因就是从安全性的方面着想,如果谁都可以拿到这个属性,你也不知道他用这个值做了什么操作,到最后就会出现一些诡异的结果。
闭包的理解
定义总是让人感觉到不接地气,我在这里举个简单的例子去解释这个问题,有可能不是很恰当,但是大概可以帮助我们去理解这个问题。
Js的整体思想是由事件驱动的。每个人的身体其实也是事件驱动的,假设现在你的大脑告诉你饿了,然后这个事件就会触发一个下外卖的回调函数,然后你会有三个选择:A公司,B公司,C公司。具体哪个公司的外卖,我们会保存在这个回调函数外部的一个变量中。代码如下:
var Seller = function() {
var seller = '';
function sellBy(seller) {
console.log("我们在用: "+ seller);
}
return {
eleme: function() {
sellBy('A公司');
},
meituan: function() {
sellBy('B公司');
},
baidu: function() {
sellBy('C公司');
}
}
};
var seller1 = Seller();
var seller2 = Seller();
seller1.eleme();
seller2.meituan();
这个时候A公司,B公司,C公司各自都有一套自己所拥有的方法,每个方法虽然都有seller这个参数,但是参数的值并不相同,他们都是各自隔离开的。就像是咱们定义的seller1和seller2,他们互相都不干扰,互不认识。
咱们在代码中其实经常会使用到闭包,只是咱们自己没有发现。因为大多数情况都是将事件与对应需要做处理的回调函数相互对应上,回调函数其实就是一个闭包。如果稍不留神,闭包带来的问题可能是灾难性的。
结论
闭包是函数和声明该函数的词法环境的组合。
可能我的理解还是比较浅的,有关于更细节的东西可以参考:[https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures