JS 闭包难点剖析

目录

JavaScript 中的作用域是什么意思?

全局作用域

函数作用域

块级作用域

闭包会在哪些场景中使用?

闭包产生的原因

作用域链

通过定时器循环输出自增的数字通过 JS 的代码如何实现?

这是为什么呢

利用 IIFE

使用 ES6 中的 let

定时器传入第三个参数


JavaScript 中的作用域是什么意思?

JavaScript 的作用域通俗来讲,就是指变量能够被访问到的范围,在 JavaScript 中作用域也分为好几种,ES5 之前只有全局作用域和函数作用域两种。ES6 出现之后,又新增了块级作用域。

全局作用域

全局变量也是拥有全局的作用域,无论你在何处都可以使用它,在浏览器控制台输入 window.vName 的时候,就可以访问到 window 上所有全局变量。

 

函数作用域

function getName () {

  var name = 'inner';

  console.log(name); //inner

}

getName();

console.log(name);

上面代码中,name 这个变量是在 getName 函数中进行定义的,所以 name 是一个局部的变量,它的作用域就是在 getName 这个函数里边,也称作函数作用域。

除了这个函数内部,其他地方都是不能访问到它的。同时,当这个函数被执行完之后,这个局部变量也相应会被销毁。所以你会看到在 getName 函数外面的 name 是访问不到的。从这点看,可以更好地理解闭包的含义:

闭包其实就是一个可以访问其他函数内部变量函数。即一个定义在函数内部的函数,或者直接说闭包是个内嵌函数也可以。

块级作用域

其实就是在 JS 编码过程中 if 语句及 for 语句后面 {...} 这里面所包括的,就是块级作用域

ES6 中新增了块级作用域,最直接的表现就是新增的 let 关键词,使用 let 关键词定义的变量只能在块级作用域中被访问,有“暂时性死区”的特点,也就是说这个变量在定义之前是不能被使用的。

闭包会在哪些场景中使用?

红宝书闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数。
MDN:一个函数和对其周围状态的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包(closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。

闭包其实就是一个可以访问其他函数内部变量的函数。即一个定义在函数内部的函数,或者直接说闭包是个内嵌函数也可以。

闭包产生的原因

作用域链

其实很简单,当访问一个变量时,代码解释器会首先在当前的作用域查找,如果没找到,就去父级作用域去查找,直到找到该变量或者不存在父级作用域中,这样的链路就是作用域链。

在创建compare()函数的时候,会创建一个预先包含全局变量对象的作用域链,这个作用域链被保存在内部的[[Scope]],如下图所示

当调用函数的时候,会为函数创建一个执行环境,然后复制函数的[[Scope]]属性中,此后还有活动对象

 

function compare(value1,value2){
  if (value1 >value2){ 
    return -1
  }else if(value1 < value2){
    return 1
  }else{
    return 0
  }
}
var res = compare(5,10)
console.log(compare.prototype)

通过定时器循环输出自增的数字通过 JS 的代码如何实现?

console.log("循环输出问题")
for(var i = 1; i <= 5; i ++){

  setTimeout(function() {

    console.log(i)

  }, 0)

}

这是为什么呢

  1. setTimeout 为宏任务,由于 JS 中单线程 eventLoop 机制,在主线程同步任务执行完后才去执行宏任务,因此循环结束后 setTimeout 中的回调才依次执行。

  2. 因为 setTimeout 函数也是一种闭包,往上找它的父级作用域链就是 window,变量 i 为 window 上的全局变量,开始执行 setTimeout 之前变量 i 已经就是 6 了,因此最后输出的连续就都是 6。

利用 IIFE

可以利用 IIFE(立即执行函数),当每次 for 循环时,把此时的变量 i 传递到定时器中,然后执行,改造之后的代码如下。

for(var i = 1;i <= 5;i++){

  (function(j){

    setTimeout(function timer(){

      console.log(j)

    }, 0)

  })(i)

}

使用 ES6 中的 let

ES6 中新增的 let 定义变量的方式,使得 ES6 之后 JS 发生革命性的变化,让 JS 有了块级作用域,代码的作用域以块级为单位进行执行。通过改造后的代码,可以实现上面想要的结果。

for(let i = 1; i <= 5; i++){

  setTimeout(function() {

    console.log(i);

  },0)

}

定时器传入第三个参数

setTimeout 作为经常使用的定时器,它是存在第三个参数的,日常工作中我们经常使用的一般是前两个,一个是回调函数,另外一个是时间,而第三个参数用得比较少。那么结合第三个参数,调整完之后的代码如下

for(var i=1;i<=5;i++){

  setTimeout(function(j) {

    console.log(j)

  }, 0, i)

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值