1
0
mirror of https://github.com/ruanyf/es6tutorial.git synced 2025-05-24 18:32:22 +00:00
es6tutorial/docs/let.md
2015-02-08 20:48:39 +08:00

298 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# let和const命令
## let命令
ES6新增了let命令用来声明变量。它的用法类似于var但是所声明的变量只在let命令所在的代码块内有效。
```javascript
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b //1
```
上面代码在代码块之中分别用let和var声明了两个变量。然后在代码块之外调用这两个变量结果let声明的变量报错var声明的变量返回了正确的值。这表明let声明的变量只在它所在的代码块有效。
for循环的计数器就很合适使用let命令。
```javascript
for(let i = 0; i < arr.length; i++){}
console.log(i)
//ReferenceError: i is not defined
```
上面代码的计数器i只在for循环体内有效。
下面的代码如果使用var最后输出的是9。
```javascript
var a = [];
for (var i = 0; i < 10; i++) {
var c = i;
a[i] = function () {
console.log(c);
};
}
a[6](); // 9
```
如果使用let声明的变量仅在块级作用域内有效最后输出的是6。
```javascript
var a = [];
for (var i = 0; i < 10; i++) {
let c = i;
a[i] = function () {
console.log(c);
};
}
a[6](); // 6
```
let不像var那样会发生“变量提升”现象。
```javascript
function do_something() {
console.log(foo); // ReferenceError
let foo = 2;
}
```
上面代码在声明foo之前就使用这个变量结果会抛出一个错误。
这也意味着typeof不再是一个百分之百安全的操作。
```javascript
if (1) {
typeof x; // ReferenceError
let x;
}
```
上面代码中由于块级作用域内typeof运行时x还没有声明所以会抛出一个ReferenceError。
总之在代码块内使用let命令声明变量之前该变量都是不可用的。这在语法上称为“暂时性死区”temporal dead zone简称TDZ
```javascript
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的“死区”。
有些“死区”比较隐蔽,不太容易发现。
```javascript
function bar(x=y, y=2) {
return [x, y];
}
bar(); // 报错
```
上面代码中调用bar函数之所以报错是因为参数x默认值等于另一个参数y而此时y还没有声明属于”死区“。
```javascript
let foo = 'outer';
function bar(func = x => foo) {
let foo = 'inner';
console.log(func()); // outer
}
bar();
```
上面代码中函数bar的参数func默认是一个匿名函数返回值为foo。这个匿名函数运行时foo只在函数体外声明内层的声明还没执行因此foo指向函数体外的声明输出outer。
注意let不允许在相同作用域内重复声明同一个变量。
```javascript
// 报错
{
let a = 10;
var a = 1;
}
// 报错
{
let a = 10;
let a = 1;
}
```
因此,不能在函数内部重新声明参数。
```javascript
function func(arg) {
let arg; // 报错
}
function func(arg) {
{
let arg; // 不报错
}
}
```
## 块级作用域
let实际上为JavaScript新增了块级作用域。
```javascript
function f1() {
let n = 5;
if (true) {
let n = 10;
}
console.log(n); // 5
}
```
上面的函数有两个代码块都声明了变量n运行后输出5。这表示外层代码块不受内层代码块的影响。如果使用var定义变量n最后输出的值就是10。
块级作用域的出现实际上使得获得广泛应用的立即执行匿名函数IIFE不再必要了。
```javascript
// IIFE写法
(function () {
var tmp = ...;
...
}());
// 块级作用域写法
{
let tmp = ...;
...
}
```
另外ES6也规定函数本身的作用域在其所在的块级作用域之内。
```javascript
function f() { console.log('I am outside!'); }
(function () {
if(false) {
// 重复声明一次函数f
function f() { console.log('I am inside!'); }
}
f();
}());
```
上面代码在ES5中运行会得到“I am inside!”但是在ES6中运行会得到“I am outside!”。这是因为ES5存在函数提升不管会不会进入if代码块函数声明都会提升到当前作用域的顶部得到执行而ES6支持块级作用域不管会不会进入if代码块其内部声明的函数皆不会影响到作用域的外部。
需要注意的是如果在严格模式下函数只能在顶层作用域和函数内声明其他情况比如if代码块、循环代码块的声明都会报错。
## const命令
const也用来声明变量但是声明的是常量。一旦声明常量的值就不能改变。
```javascript
const PI = 3.1415;
PI // 3.1415
PI = 3;
PI // 3.1415
const PI = 3.1;
PI // 3.1415
```
上面代码表明改变常量的值是不起作用的。需要注意的是,对常量重新赋值不会报错,只会默默地失败。
const的作用域与let命令相同只在声明所在的块级作用域内有效。
```javascript
if (condition) {
const MAX = 5;
}
// 常量MAX在此处不可得
```
const声明的常量也与let一样不可重复声明。
```javascript
var message = "Hello!";
let age = 25;
// 以下两行都会报错
const message = "Goodbye!";
const age = 30;
```
由于const命令只是指向变量所在的地址所以将一个对象声明为常量必须非常小心。
```javascript
const foo = {};
foo.prop = 123;
foo.prop
// 123
foo = {} // 不起作用
```
上面代码中常量foo储存的是一个地址这个地址指向一个对象。不可变的只是这个地址即不能把foo指向另一个地址但对象本身是可变的所以依然可以为其添加新属性。如果真的想将对象冻结应该使用Object.freeze方法。
```javascript
const foo = Object.freeze({});
foo.prop = 123; // 不起作用
```
上面代码中常量foo指向一个冻结的对象所以添加新属性不起作用。