闭包和let块级作用域

还是先从一个题目开始:

写一个隔1s输出数组的一项的函数。

如果可以用ES6语法,则可以这么写:

function print (arr) {
    for (let i = 0; i < arr.length; i++) {
        setTimeout(() => {
            console.log(arr[i])
        }, 1000 * i);
    }
}

但是如果把这里的let改成var,则输出就会变成一连串的undefined

有同学很快想到了这是闭包啊,因为setTimeout把函数加入到microqueue中,所以等到setTimeout的函数体执行时,i已经走完了for循环,变成了arr.lengtharr[arr.length]显然是undefined。

简单修改一下,变成ES5的语法。

function print (arr) {
    for (var i = 0; i < arr.length; i++) {
        (function (index) {
            setTimeout(() => {
                console.log(arr[index])
            }, 1000 * index);
        })(i);
    }
}

其实就是利用闭包是向父级作用域寻找值的特性,给i包装一层作用域,把i存起来。

闭包概念还请翻看之前的一篇blog-闭包和类

到这里闭包的理解应该差不多了,而今天的关键点在于——

let做了什么?

阮一峰老师的《ECMAScript 6》入门里给出[定义](http://es6.ruanyifeng.com/#docs/let

ES6 新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

他提到了let的几个特性:

  1. 只存在于块级作用域中
  2. 不存在变量提升
  3. 暂时性死区
  4. 不允许重复声明

这里我不再赘述,大家可以仔细阅读一下阮一峰老师的书。

我更感兴趣的是,在ES5的语法中,如何模拟let这种块级作用域的效果。这个时候,应该让babel出场了。

打开这个链接:可以看到转换后的代码。

"use strict";

function print(arr) {
  var _loop = function _loop(i) {
    setTimeout(function () {
      console.log(arr[i]);
    }, 1000 * i);
  };

  for (var i = 0; i < arr.length; i++) {
    _loop(i);
  }
}

其实可以对比发现,babel转换后的代码和我们上面写的ES5实现其实是一样的。

大概就是通过对let绑定的块级作用域加一个函数,把let声明的参数,通过函数传入,达到块级作用域的效果。

大家可以在babel试一下let的其他特性,转移出的ES5语法并不能实现有的特性,比如暂时性死区。

完,感谢阅读。

原文地址:https://www.cnblogs.com/liuyongjia/p/10623665.html

时间: 2024-11-08 23:43:38

闭包和let块级作用域的相关文章

javascript中的闭包、模仿块级作用域和私有变量

闭包是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式为:在一个函数内部创建另一个函数. "当某个函数被调用时,会创建一个执行环境(execution context)及相应的作用域链.然后,使用arguments和其他命名参数的值来初始化函数的活动对象(activation object).但在作用域链中,外部函数的活动对象始终处于第二位,外部函数的外部函数的活动对象出于第三位.....直至作用域链终点的全局执行环境." function creawteCompariso

JS 模仿块级作用域

function outputNumbers(count) { for (var i=0; i<count; i++) { console.log(i); } var i;  // 重新声明变量 console.log(i);  // 计数 } 上述中,重复声明了i变量,但是JS从来不会告诉你是否多次声明了同一变量:遇到这种情况,它只会对后续的声明视而不见 不过,它会执行后续声明中变量的初始化.匿名变量可以用来模仿块级作用域并避免这个问题. (function() { // 这里是块级作用域(私

ES6之块级作用域

一.前言 在ECMAScript6(以下简称ES6)之前,ECMAScript的作用域只有两种: 1.  全局作用域: 2.  函数作用域. 正是因为有这两种作用域,所以在JavaScript中出现一术语--“变量提升(hoisting)”. 如下: function func(){ console.log(test); var test = 1; }; func(); 在node环境执行上述代码,结果为: 之所以为’undefined’,原因就在于‘变量提升’,在进入func函数时,将所有通过

模仿块级作用域

在JavaScript中没有块级作用域的概念.这就意味着在块语句中定义的变量,实际上是在包含函数中而非语句中创建的. <script> for(var i=0;i<5;i++) { } alert(i); </script> 就上面这个例子中,可以弹出i是5,因为for语句事实上是包含在window.onload= function(){}这个函数中,alert也在这个函数中. 而在java.c++中i只会在for循环的语句块中定义,循环一但结束,变量i就会被销毁.而在Jav

匿名函数模仿块级作用域

问题 [无块级作用域]JavaScript中没有块级作用域的概念.这意味着在块语句中定义的变量,实际上是在包含函数中而非语句中创建的. function outputNumbers(count){ for(var i = 0; i < count; i++){ alert(i); } console.log(i); } outputNumbers(2);//弹出0,1输出2 //变量i是定义在outputNumbers()的活动对象中的,因此从它有定义开始,就可以在函数内部随时访问它. 函数中的

js没有块级作用域但有函数作用域

任何一对花括号中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域. 作用域永远都是任何一门编程语言中的重中之重,因为它控制着变量与参数的可见性与生命周期.首先我们理解两个概念:块级作用域与函数作用域. 什么是块级作用域呢? 任何一对花括号({和})中的语句集都属于一个块,在这之中定义的所有变量在代码块外都是不可见的,我们称之为块级作用域. 函数作用域就是定义在函数中的参数和变量在函数外部是不可见的. 大多数类C语言都拥有块级作用域,JS却没有.请看下文de

let块级作用域

let是es6中新加的作用域,即块级作用域. var申明的变量要么全局,要么函数级,而let允许把变量的作用域限制在块级域中,这里的块级可以是()内,或{}内. 示例: code_1: 1 "use strict"; 2 var a = []; 3 for(let i=0; i<10; i++){ 4 a[i] = function(){ 5 console.log(i); 6 } 7 } 8 a[6]();//结果为6 code_2: 1 var a = []; 2 for(v

js模仿块级作用域

var i = 5; var i; alert(i); 当一个变量被声明赋值之后,如果再对其声明,则不会改变上一次赋值后的值,所以i的值还是5,只有重新赋值之后,才会改变,如: var i = 5; var i = 6; alert(i); i现在的值是6: JS中没有块级作用域,如果想要块级作用域的效果,可以进行模仿,如: (function () { for (var i = 0; i < 5; i++) { } })(); alert(i); //出错 这方法可以理解为把块级作用域放在一个

JS函数表达式 -- 模仿块级作用域

JavaScript没有块级作用域的概念.这意味着在语句中定义的变量,实际上是在包含函数中而非语句中创建的. 1 function outputNumbers(count){ 2 for(var i=0; i<count; i++){ 3 alert("In: " + i); 4 } 5 var i; 6 alert("Out: " + i); 7 } 8 outputNumbers(2); //In: 0 In: 1 Out:2 在这个函数中定义了一个for