zoukankan      html  css  js  c++  java
  • 闭包&作用域链&let

    1. 概念

    闭包函数:声明在一个函数中的函数,叫做闭包函数。
    闭包:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)之后。
    

    2. 特点

    • 可以实现在外部访问函数内部变量。
    • 可以避免全局污染。
    • 局部变量常驻内存,会造成内存泄漏。

    3. 作用域链

    在js中,常见作用域分为全局作用域、函数作用域、块级作用域,作用域有上下级关系,上下级关系的确定就看函数是在哪个作用域下创建的,如果要搞清楚闭包问题,就需要了解作用域链。

    如图,当c函数中没有x,y变量时就会按照作用域链往上级找直到找到同名变量,作用域链:c函数作用域->b函数作用域->a函数作用域->全局作用域

    作用域链

    4.案例

       for (var i = 0; i < 5; i++) {
           setTimeout(function () {
               console.log(i++);
           }, 2000)
       }
       console.log(i);
       /* 输出 5 5 6 7 8 9 */
    

    执行步骤

    1.全局执行上下文进入执行栈
    2.每次执行1个for循环将会发生:将1个setTimeout任务放到浏览器定时触发线程,到达时间后将回调放入任务队列(事件循环),i++
    4.执行for循环外的输出任务(输出5)
    5.全局执行上下文出栈,同步代码执行完毕
    6.任务队列的任务依次进栈->执行(console.log(i++))->出栈(每次栈里只有一个任务)(5 6 7 8 9)
    7.执行完毕

    由于定时器回调函数未定义i,输出的i根据作用域链找到全局的i(由于存在闭包,全局执行完 i不会被回收),回调执行时i已经是变成5了。

    解决方法1:使用自执行函数

    for (var i = 0; i < 5; i++) {
        (function (x) {
            // var x = i;相当于有这么一句
            setTimeout(function () {
                console.log(x++);
            }, 2000)
        })(i);
    }
    console.log(i);/* 结果 5 0 1 2 3 4 */
    

    执行步骤与修改前大致相同,区别在于每次循环都会将自执行函数进栈出栈,也就是说每个自执行函数对应的x在不同内存空间的,定时器访问的x是各个自执行函数中的独有x,而不是全局的i,所以输出的值不会受i后续变化的影响。
    解决办法2:使用let声明

    for (let i = 0; i < 5; i++) {
        setTimeout(function () {
            console.log(i++);
        }, 2000)
    }
    /* 输出 0 1 2 3 4 */
    

    首先说明let和var的区别,let相对于var具有不支持变量提升、不可重复定义、块级作用域等特点,由于let块级作用域的特点,for循环中每一层循环对应一块作用域,每个回调函数寻找的上级的i也是属于不同作用域的。

  • 相关阅读:
    Spring入门之通过注解 处理 数据库事务
    Spring 入门之-dao使用jdbcTemplate(注入过程)
    spring入门之JdbcTemplate 操作crud
    npm 包 升降版本
    vue-cli初始化一个项目
    <meta http-equiv="refresh" content="0; url=">
    vue-router的两种模式的区别
    Webstorm 的 Tab 键怎样调整缩进值? 调节成缩进成2个空格或者4个空格
    去抖函数 节流函数
    创建一个vue项目,vue-cli,webpack
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/12726199.html
Copyright © 2011-2022 走看看