第二章 let 和 const 命令

// let 与 var

/* 一 */

    {

let a =12;

        var b =10;

    }

//console.log(a) // a is not defined

//console.log(b) // b = 10

// let 声明的变量只再其所在的代码块中有效的

/* 二 */

// for 循环的计数器就很适合使用let 命令

    for (let i =0; i <10; i++) {

// console.log(i) // 0,1,2,3,4,5,6,7,8,9

    }

// console.log(i) // i is not defined -->原因:使用在for 循环里面使用let 定义的计数器 i ,只能在for循环体内有效,在循环体外面就会报错。

  var a = [];

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

a[i] =function () {

console.log(i)

}

}

a[6] ();// 10,

// 变量 i 是 var 声明的,在全局范围内都有效的,所以全局只有一个变量 i 。

// 每一个循环,变量 i 的值都会发生改变,而循环内,被赋给数组 a 的函数内部的 console.log(i)中的 i 指向全局的 i.

// 也就是说数组 a 的成员中的 i 指向的都是 同一个 i,导致运行时输出的是最后一轮的 i 的值,也就是10.

// 也可以理解为,全局变量i的值在循环里是不断变化的,相当于全局变量 i不断的被声明和覆盖并赋值给 数组 a,

// 如果使用 let ,声明的变量仅在块级作用域内有效,最后将输出 6

    var a = [];

    for(let i =0; i <10; i++){

a[i] =function () {

console.log(i);

        }

}

a[6] (); // 6

// 上面的代码中,变量 i 是let声明的,当前的 i 只在本轮循环有效。所以每一次循环的 i,其实都是一个新的变量,于是最后输出的是 6 .

// 大家可能会问,如果每一次循环的变量 i都是重新声明的,那么它怎么知道上一轮循环的值,从而计算出本轮循环的值呢?

// 这是因为JavaScript引擎内部会记住上一轮循环的值,初始化本来的变量 i 时,就在上一轮循环的基础上进行计算。

// 另外 for 循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。

    for(let i =0; i <3; i++){

let i ='abc';

        console.log(i)

}

// 输出3次 abc

// 这表明函数内部的变量 i 与循环变量 i 不在同一个作用域,而有各自单促的作用域。

/* 三 */  //不存在变量提升

// var 命令发生 “变量提升” 现象,即变量可以在声明之前使用,值为 undefined。这种现象多多少少是有些奇怪的。

// 按照一般的逻辑,变量应该在声明语句之后才可以使用的

// 为了纠正这种现象,let命令改变了语法行为,它所声明的变量一定要在声明后使用,否则便会报错的

// var 的情况

    console.log(foo)// undefined

    var foo =2

    // 类似于 以下

// var foo;// 变量被声明了。但是没有赋值,输出没有赋值的变量就是undefined;

// console.log(foo)

// foo = 2;// 在此处变量赋值

// let 的情况

// console.log(bar); // 报错

// let bar = 2;

/*

在以上代码中,变量foo用var 命令声明会发生变量提升,即脚本开始运行时,变量foo便已经存在,但是没有值,所有会输出undefined 。

变量bar 使用 let命令声明则不会发生变量提升。这表示在声明它之前,变量bar是不存在的,这时如果用到它,就会抛出一个错误。

*/

/* 四 暂时性死区 */

    var tmp =123;

    if (true) {

tmp ='abc'; // ReferenceError 报错了

// let tmp;

    }

// 只要块级作用域内 存在 let 命令,它所声明的变量就“绑定(binding)”这个区域,不再受外部的影响。

// 上面的代码中存在全局变量 tmp, 但是块级作用域内let 又声明了一个局部变量tmp,导致后者绑定这个块级作用域.

// 所以在let声明变量前,对tmp赋值会报错的。

// ES6明确规定,如果区块中存在 let 和 const 命令,则这个区块对这些命令声明的变量,从一开始就形成封闭作用域。只要在声明之前就使用这些变量。就会报错。

// 总之,在代码块内,使用let命令声明变量之前。该变量都是不可用的。这在语法上称为“暂时性死区(temporal dead zone简称 TDZ)”.

    if (true) {

// TDZ开始

// tmp = 'abc' // ReferenceError

// console.log(tmp); // // ReferenceError

        let tmp; // TDZ结束

        console.log(tmp); // undefined;

        tmp =123

        console.log(tmp)//123

    }

/*

* 上面的代码中,在 let 命令声明变量 tmp 之前,都属于 变量 tmp的 “死区”

* “暂时性死区” 也意味着 typeof 不再是一个百分之百安全的操作。

*

* */

    typeof x; // ReferenceError

// let x;

/*

* 上面的代码中,变量x使用 let 命令声明,所以在声明之前都属于 x 的“死区”,只要用到该变量就会报错。

* 因此,typeof 运行时就会抛出一个ReferenceError.

* */

// 如果一个变量根本没有被声明,使用typeof 反而不会报错。

    console.log(typeof undeclared_variable)// undefined

/*

* 上面的代码中 变量undeclared_variable是不存在的变量。结果返回:undefined,

* 所以在没有 let 之前,typeof 运算符是百分百安全的,永远不会报错。现在这一点不成立了

* 这样的设计是为了让大家养成良好的编程习惯。变量一定要在声明之后使用。否则就会报错。

* */

// 有写 “死区” 比较隐蔽,不太容易发现的,如下:

// function bar (x = y, y = 2) {

//    return [x, y];

// }

//bar(); // 报错了

/*

* 上面的代码中,调用bar函数之所以报错(某些实现可能不报错),

* 是因为参数 x 的默认值等于另一个参数 y ,而此时 y 还没有声明,属于 “死区”。

* 如果 y 的默认值是 x,就不会报错。因为此时 x 已声明。

* */

    function bar(x =2, y = x) {

return [x, y];

    }

console.log(bar()); //[2, 2]

// 下面的代码也会报错,与 var 的行为不同

    var x = x; // 不会报错

// let y = y ; // 报错

/*

* 以上代码报错也是因为暂时性死区,使用let声明变量时,只要变量在还没有声明前使用的,就会报错。

* 以上示例就属于这种情况,在变量 x 的声明语句还没有执行完成前就尝试获取 x 的值,导致出现“x未定义”的错误。

* */

/* 五,不允许重复声明*/

// let 不允许在相同作用域内重复声明同一个变量

// 报错一

    function a() {

let a  =10;

        var a  =1;

    }

// a()

// 报错二

    function a() {

let a =10;

        let a =1;

    }

// a()

// 不能在函数内部重新声明参数

    function func (arg) {

let arg;

    }

// func()

// 不报错

    function func (arg) {

{

let arg;

        }

}

func();

/*六 块级作用域*/

// ES5 只有全局作用域和函数作用域,没有块级作用域,这个导致很多场景不合理。

// 第一种场景,内层变量可能会覆盖外层变量。

    var tmp =new Date()

function f() {

// var tmp; // 内层的变量tmp声明提前了。导致内层的var tmp; 覆盖了外层的 tmp = new Date()

        console.log(tmp); // undefined

        if (false) {

var tmp ='Hello World'

        }

}

console.log(f()); // undefined

// 第二种场景,用来计数的循环变量泄露为全局变量

    var s ='hello';

    for(var i =0; i < s.length; i++) {

console.log(s[i]); // h e l l o

    }

console.log(i); // 5

/*

* 上面的代码中,变量 i 只用来控制循环,但是循环结束后,它并没有消失,而是泄露成了全局变量。

*  */

// ES6 的 块级作用域

// let 实际上为JavaScript新增了块级作用域

    function f1 () {

// 外层

        let n =5;

        if (true) {

// 内层

            let n =10;

        }

// 外层代码块不受内层代码块的影响。

        console.log(n); //5; // 如果用 var 定义的话,输出的就是 10了。

    }

f1();

    // ES6 允许块级作用域的任意嵌套 (外层作用域是无法读取内层作用域的变量的。)

    {{{{{let insane ="Hello World"}}}}}

// 外层作用域是无法读取内层作用域的变量的

    {{{{

{let insane ="Hello World"}

// console.log(insane); // 报错的

    }}}}

// 内层作用域可以定义外层作用域的同名变量的

    {{{{

let insane ="Hello World"

        {let insane ="Hello World"}

}}}}

// ES5 规定,函数只能在顶级作用域或者函数作用域之中声明的,不能在块级作用域中声明。

// 但是浏览器没有遵守这个规定,为了兼容以前的代码,还是支持在块级作用域之中声明函数。因此以下的情况都能运行,不会报错。

// 情况一

    if (true) {

function f () {

}

}

// 情况二

    try {

function f() {}

}catch (e) {

}

// ES6 引入了块级作用域,明确允许在块级之中声明函数。

// ES6 规定,在块级作用域之中,函数声明语句的行为类似于 let, 块级作用域之外不可引用。

    function f () {console.log('I am outside!');}

(function () {

if (false) {

// 重复声明一次函数 f

            function f() {console.log('I am inside!');}

}

// f();

    }())

// 在ES6 中会报错

// 应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式得形式。而不是函数声明语句

// 函数声明语句

    {

let a ='secret'

        function f() {

return a

}

}

// 函数表达式

    {

let a ='secret';

        let f =function () {

return a;

        }

}

// do表达式

// 本质上,块级作用域是一个语句,将多个操作封装再一起,没有返回值。

// 现在有个提案,使得块级作用域可以变为表达式,即可以返回值,办法就是再块级作用域之前加上 do.使他变为 do表达式。

// let x = do {

//    let t = f();

//    t * t + 1;

// }

    // console.log(x)

const 命令

/* const 命令 */

// 基本用法

// const 声明一个只读的常量,一旦声明,常量的值就不能改变。

    const PI =3.1415;

    console.log(PI)

// PI = 3; // 改变常量的值会报错。

// const foo;

// const声明的常量不得改变值。这意味着,const一旦声明常量,就必须立即初始化,不能留到以后赋值。

// 顶层对象在浏览器环境中指的是 window 对象。在node 环境中指的是global对象。

// 在ES5中,顶层对象的属性与全局变量是等价的。

// 在ES6 中规定 let,const,class命令声明的全局变量不属于顶层对象的属性。

    var a =1

    console.log(window.a)// 1

    console.log(this.a)// 1

    console.log(self.a)

// 在浏览器中顶层对象是 window .但是 self 也指向顶层对象。但是 Node中没有 self .

// 在 Node 中,顶层对象是 global .但是其他环境都不支持。

// Es6

    let b =1;

    console.log(window.b)// undefined

// 上面的代码中,全局变量 a 是由 var命令声明,所以它是顶层对象的属性。

// 全局变量 b 是由 let 声明的。所以它不是顶层对象的属性。返回 undefined.

/*

* 在全局环境中,this会返回顶层对象。但是在Node 模块和 ES6 模块中,this 返回的是当前模块。

* 对于函数中的this.如果函数不是作为一个对象的方法运行,而是单纯作为函数运行,this.会指向顶层对象。但是在严格模式下,this.会返回undefined

*

*

    * */

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