小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本想写call、apply和bind的原理,写着发现他们与this相关,我就去看this,然后发现this和作用域相关,最后就看到了闭包。这里看的书籍《你不知道的JavaScript(上卷)》。
首先
我甩你一段代码:
function father() {
var fatherVal = 'father'
son()
function son() {
var sonVal = 'sonVal'
console.log(fatherVal,' ',sonVal); //father sonVal
}
}
father()
复制代码
请问这个是闭包
嘛?
行行行,我知道你们说不是。下面这个呢?(自问自答的乐趣)。
function father() {
var fatherVal = 'father'
function son() {
var sonVal = 'sonVal'
console.log(fatherVal,' ',sonVal); //father sonVal
}
return son
}
var result = father()
result()
复制代码
好吧好吧这个的确是闭包,那么你们知道下面这个嘛?
var result
function father() {
var fatherVal = 'father'
function son() {
var sonVal = 'sonVal'
console.log(fatherVal,' ',sonVal); //father sonVal
}
result = son
}
father()
result()
复制代码
这个你也知道,那么下面这个呢?
function father() {
var fatherVal = 'father'
function son() {
var sonVal = 'sonVal'
console.log(fatherVal,' ',sonVal); //father sonVal
}
result(son)
}
function result(fn) {
fn()
}
father()
复制代码
啊!这个你也知道,放杀手锏了。
function father(value) {
setTimeout(function(){
console.log(value);
}, 1000)
}
father('fatherValue')
复制代码
累死了,什么!!这个你也知道,对不起!浪费了您几分钟时间。
你们都知道行了吧,我就皮,我写给以后的自己看。既然这些都是闭包那么它们有啥共同性呢?
正文
这里先来稍微讲解下作用域把,然后再引出闭包。
js作用域
我反手一段代码丢上去:
var wd = 'window'
function father(msg) {
var fatherValue = 'father'
function son() {
var sonValue = 'son'
console.log();
console.log(wd,'--',fatherValue,'--',msg,'--',sonValue); //window -- father -- 我是father的参数 -- son
}
son()
}
father('我是father的参数')
复制代码
这个代码大家都能看的懂,但是我还是画了一张图,大家就多浪费几秒钟看一眼。
这就是常规的作用域, 有点冒泡的感觉。
闭包
我再反手一段代码:
var wd = 'window'
function father() {
son()
function son() {
var sonValue = 'son'
}
}
son()
console.log(sonValue);
复制代码
这就是一段错误的代码
,从之前的作用域中我们是son
中可以拿
到window
和father
作用域上面的值
。这里我们的window
来访问father
和son
的值因此就报错。所以闭包
它就来了,让本不应该访问
到的作用域现在可以访问到
,就是由于这个外部可以访问
,当外部没有执行,所以函数作用域未作销毁
。
开启讲解前面的代码
前面两个,一个只是正常的嵌套,另一个是return出去。这两就不讲了。
直接讲解第三个:
var falseSon
function father() {
var fatherVal = 'father'
function son() {
var sonVal = 'sonVal'
console.log(fatherVal,' ',sonVal); //father sonVal
}
falseSon = son
}
father()
falseSon()
复制代码
编译器的步骤
: (这里可能有些人不理解为什么先产生函数, 我就是这么豪横,你们就不理解着吧)
- 先产生出
father
这个函数。 - 声明一个
falseSon
变量。 执行
father函数。- 再产生出
son
函数。 - 在声明
fatherVal
变量。 - 对
fatherVal
变量进行赋值
'father'。 - 对
falseSon
变量进行赋值
son。 - 执行
falseSon
函数
从这里我们看出在全局作用域
中我们本不应该能访问到son的,因为son是属于father的作用域。但是,就是因为恶人(闭包)
找了东西把son复制给了falseSon
出来给自己用,这个复制的很厉害拥有了son的所以技能(功能),然后因为有falseSon所以垃圾回收就没有把father给收走
。
杀手锏
function father(value) {
setTimeout(function son(){
console.log(value);
}, 1000)
}
father('fatherValue')
复制代码
这里跟上面的对比是不是有点难看出来它是一个闭包,只要我们找准谁拥有假孩子。
呔,我逮住了setTimeout,你这小子有假孩子(参数),这小子把假孩子藏起来了,还好我有火眼金金。
function father() {
var fatherVal = 'father'
function son() {
var sonVal = 'sonVal'
console.log(fatherVal,' ',sonVal); //father sonVal
}
result(son)
}
function result(falseSon) {
falseSon()
}
father()
复制代码
这不就是和它一样嘛,setTimeout 就相当于 result。变身:
function father(value) {
function son(){
console.log(value);
}
setTimeout(son, 1000)
}
father('fatherValue')
复制代码
厉害吧,不讲啦!就这样吧。
结论
最后来个总结:
解释者:想象下一个大盒子
里面包裹中盒子
,中盒子包裹小盒子
。我在大盒子外面本来拿不到小盒子,有了闭包我可能直接拿到小盒子
。
看者: 盒子盒子的,我看你就像一个盒子。
这次真溜溜。
参考
《你不知道的JavaScript(上卷)》