从闭包到es6变量声明

之前学习闭包的时候碰到一道题

var arr = [];
for (var i = 0; i < 10; i++){
    arr[i] = function(){
        return i
    }
}
console.log(arr[3]())  //10

var声明的变量没有块级作用域。所以实际上这里的i实际上是定义在全局作用域下的

console.log(window.i) //10

函数在循环之后调用。所以从作用域链中查找i,结果为10

如果要输出3,可以使用立即执行函数修改

var arr = []
for (var i = 0; i < 10; i++){
    arr[i] = (function(j){
        return function(){
            return j
        }
    })(i)
}
console.log(arr[3]()) //3

立即执行函数传入i,形参j在当前作用域保存了i的值。形成一个闭包。

mdn对闭包的解释

闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量

这道题有个更优雅的解法,就是我们今天的主角let

var arr = []
for (let i = 0; i < 10; i++){
    arr[i] = function(){
        return i
    }
}
console.log(arr[3]())  //3

阮一峰老师的是这么解释的:

上面代码中,变量ilet声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。你可能会问,如果每一轮循环的变量i都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值?这是因为 JavaScript 引擎内部会记住上一轮循环的值,初始化本轮的变量i时,就在上一轮循环的基础上进行计算

babel转换为ES5是这样的:

"use strict";

var arr = [];

function _loop(i) {
    arr[i] = function () {
        return i;
    };
};

for (var i = 0; i < 10; i++) {
    _loop(i);
}

console.log(arr[3]());

我的理解是创建一个函数,生成一个闭包存储i。所以每次遍历就能得到对应的i

关于闭包,我觉得还有一个很重要的题目

function makeCounter() {
  var count = 0

  return function() {
    return count++
  };
}

var counter = makeCounter() 
var counter2 = makeCounter();
// 执行函数并赋值给counter,counter2
// counter, counter2对应的是不同的函数
// 他们都初始化了一个 count = 0
// 之前按引用类型指针理解,所以错了

console.log( counter() ) // 0
console.log( counter() ) // 1

console.log( counter2() ) // 0
console.log( counter2() ) // 1

let 命令总结

  1. 块级作用域

    {
        let a = 10;
        var b = 1;
    }
    a // a is not defined
    b // 1
    
  2. 在全局作用域下let声明的变量并不会绑定到window上

    let a = 1;
    var b = 2;
    window.a //undefined
    window.b //2
    
  3. for循环除了上文提及的特性,还有一个特点就是父子作用域

    for (let i = 0; i < 3; i++){
        let i = 'abc';
        console.log(i)
    }
    
    
  4. 不存在变量提升。所以一定要先声明再使用

  5. 暂时性死区
    ES6 明确规定,如果区块中存在letconst命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。

    var tmp = 123;
    if (true){
        tmp = 'abc'; //ReferenceError
        let tmp;
    }
    
  6. 不可重复声明

const 命令总结

  1. const声明一个只读的常量。改变值会报错,只声明不赋值也会报错。其他特性与let相同

  2. 本质const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于引用类型来说,保存的只是指针。所以可以改变对象,但不能改变指针

    const foo = {}
    foo.prop = 123  //ok
    foo = {} //报错
    

参考资料

阮一峰-es6入门

怎么理解for循环中用let声明的迭代变量每次是新的变量?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容