💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## let和const命令 > 1.let 命令 > 2.块级作用域 > 3.const命令 ### let命令 1.用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效 ~~~ { let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1 ~~~ 2.不存在变量提升 ~~~ // var 的情况 console.log(foo); // 输出undefined var foo = 2; // let 的情况 console.log(bar); // 报错ReferenceError let bar = 2; ~~~ 3.暂时性死区 ~~~ var tmp = 123; if (true) { tmp = 'abc'; // ReferenceError let tmp; } ~~~ 4.不允许重复声明 ~~~ // 报错 function func() { let a = 10; var a = 1; } // 报错 function func() { let a = 10; let a = 1; } ~~~ ### 块级作用域 > let实际上为 JavaScript 新增了块级作用域。 一:ES5只有全局作用域和函数作用域,没有块级作用域,会带来以下问题: 1. 变量提升导致内层变量可能会覆盖外层变量 ~~~ var a = 5; function func() { console.log(a); if (true) { var a = 1; } } func(); // undefined ~~~ 2) 用来计数的循环变量泄露为全局变量 ~~~ for (var i = 0; i < 10; i++) { console.log(i); } console.log(i); // 10 ~~~ 二:ES6块级作用域 * ES6允许块级作用域的任意嵌套。外层作用域无法读取内层作用域的变量。 ~~~ {{{{ {let i = 6;} console.log(i); // 报错 }}}} ~~~ * 内层作用域可以定义外层作用域的同名变量。 ~~~ {{{{ let i = 5; {let i = 6;} }}}} ~~~ 三: 块级作用域的出现使得立即执行函数不再需要。 > 立即执行函数: ~~~ (function() { var i = 5; })(); ~~~ > 块级作用域 ~~~ { let i = 5; } ~~~ 四:块级作用域与函数声明 ES5规定,函数只能在顶层作用域和函数作用域之外声明,不能在块级作用域中声明。 ~~~ if (true) { function func() {} } try { function func() {} } catch { } ~~~ 上面两种函数声明在ES5中都是非法的,但是浏览器没有遵守这一规定,还是支持在块级作用域中声明函数,因此以上两种情况实际都能运行,不会报错;但是,在严格模式下,还是会报错。 ~~~ 'use strict': if (true) { function func() {} // 报错 } ~~~ ES6引入了块级作用域,明确允许在块级作用域中声明函数。 ~~~ if (true) { function func() {} // 不报错 } ~~~ ES6还规定,在块级作用域中,函数声明的行为类似于let,在块级作用域之外不可引用。 ES6中,函数声明会提升到函数作用域的头部。 ~~~ function func() { console.log('out'); } (function() { if (false) { function func() { console.log(‘in’); } } func(); })(); ~~~ 在ES5中输出:in 在ES6中输出:out > 但是在浏览器中执行的时候呢会像如下一样,导致报错! ~~~ function func() { console.log(‘out’); } (function() { var func = undefined; if (false) { function func() { console.log(‘in’); } } func(); })(); ~~~ >>如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6 在附录 B里面规定,浏览器的实现可以不遵守上面的规定,有自己的行为方式。 1.允许在块级作用域内声明函数。 2.函数声明类似于var,即会提升到全局作用域或函数作用域的头部。 同时,函数声明还会提升到所在的块级作用域的头部 考虑到环境导致的行为差异太大,应该避免在块级作用域内声明函数。如果确实需要,也应该写成函数表达式,而不是函数声明语句 ~~~ // 函数声明语句 { let a = 'secret'; function f() { return a; } } // 函数表达式 { let a = 'secret'; let f = function () { return a; }; } ~~~ 另外,还有一个需要注意的地方。ES6 的块级作用域允许声明函数的规则,只在使用大括号的情况下成立,如果没有使用大括号,就会报错。 ~~~ // 不报错 'use strict'; if (true) { function f() {} } // 报错 'use strict'; if (true) function f() {} ~~~ ### const 语法 1. const声明一个只读的常量。一旦声明,常量的值就不能改变。 2. const一旦声明变量,就必须立即初始化,不能留到以后赋值。 3. const的作用域与let命令相同:只在声明所在的块级作用域内有效。 4. const命令声明的常量也是不提升, 只能在声明的位置后面使用 5. const声明的常量,也与let一样不可重复声明。 ~~~ if (true) { console.log(MAX); // ReferenceError const MAX = 5; } ~~~ ~~~ var message = "Hello!"; let age = 25; // 以下两行都会报错 const message = "Goodbye!"; const age = 30; ~~~ 6.需要注意的是const只是不允许需改内存地址,对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。 对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了 ~~~ const foo = {}; // 为 foo 添加一个属性,可以成功 foo.prop = 123; foo.prop // 123 // 将 foo 指向另一个对象,就会报错 foo = {}; // TypeError: "foo" is read-only ~~~ ~~~ const a = []; a.push('Hello'); // 可执行 a.length = 0; // 可执行 a = ['Dave']; // 报错 ~~~ ### 课后习题 1.请说出下面的程序执行后返回的值? ~~~ { let i =7; } conosle.log(i);//打印出什么? { var i = 7; } console.log(i);//打印出什么 ~~~ 2.下面的代码有哪些问题? ~~~ const x; x = 2; ~~~ ~~~ const b = 2; b = 3; ~~~ ~~~ let i = 3 { let i = 4; let i = 5; { let i = 6; } } ~~~ ~~~ { j = 1; let j = 3; } ~~~