逗比的我,为你讲“闭包”。

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本想写call、apply和bind的原理,写着发现他们与this相关,我就去看this,然后发现this和作用域相关,最后就看到了闭包。这里看的书籍《你不知道的JavaScript(上卷)》。

WeChat63aaa71a6e3fdd36d262bfcf5ce25c17.png

首先

我甩你一段代码:

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')
复制代码

累死了,什么!!这个你也知道,对不起!浪费了您几分钟时间。

Background (3).png

扫描二维码关注公众号,回复: 13155190 查看本文章

你们都知道行了吧,我就皮,我写给以后的自己看。既然这些都是闭包那么它们有啥共同性呢?

Background (8).png

正文

这里先来稍微讲解下作用域把,然后再引出闭包。

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的参数')
复制代码

这个代码大家都能看的懂,但是我还是画了一张图,大家就多浪费几秒钟看一眼。

0cdb43b2acfb78bc0a44bae7943a410.png 这就是常规的作用域, 有点冒泡的感觉。

闭包

我再反手一段代码:

var wd = 'window'
function father() {
  son()
  function son() {
    var sonValue = 'son'
  }
}

son()
console.log(sonValue);
复制代码

这就是一段错误的代码,从之前的作用域中我们是son中可以windowfather作用域上面的。这里我们的window来访问fatherson的值因此就报错。所以闭包它就来了,让本不应该访问到的作用域现在可以访问到,就是由于这个外部可以访问,当外部没有执行,所以函数作用域未作销毁

开启讲解前面的代码

前面两个,一个只是正常的嵌套,另一个是return出去。这两就不讲了。

直接讲解第三个:

var falseSon
function father() {
  var fatherVal = 'father'
  function son() {
    var sonVal = 'sonVal'
    console.log(fatherVal,'  ',sonVal); //father    sonVal
  }
  falseSon = son
}

father()
falseSon()
复制代码

编译器的步骤: (这里可能有些人不理解为什么先产生函数, 我就是这么豪横,你们就不理解着吧)

  1. 先产生出father这个函数。
  2. 声明一个falseSon变量。
  3. 执行father函数。
  4. 再产生出son函数。
  5. 在声明fatherVal变量。
  6. fatherVal变量进行赋值'father'。
  7. falseSon变量进行赋值son。
  8. 执行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')
复制代码

厉害吧,不讲啦!就这样吧。

Background (5).png

结论

Background (6).png

最后来个总结:

解释者:想象下一个大盒子里面包裹中盒子,中盒子包裹小盒子。我在大盒子外面本来拿不到小盒子,有了闭包我可能直接拿到小盒子

看者: 盒子盒子的,我看你就像一个盒子。

这次真溜溜。

Background (7).png

参考

《你不知道的JavaScript(上卷)》

猜你喜欢

转载自juejin.im/post/7013542556138995748