|
13 | 13 |
|
14 | 14 | 除此之外,我(作者:[Manuel Beaudru](https://github.com/mbeaudru))偶尔会写上一些我的小技巧,也会注意提示这只是我的个人提议。 |
15 | 15 |
|
16 | | -> **注:** 这篇文档里提到的大多数概念来自于目前最新的 JavaScript(ES2015,即 ES6),你可以在[这里](http://es6-features.org)新增的特性,网站做得很棒。 |
| 16 | +> **注:** 这篇文档里提到的大多数概念来自于目前最新的 JavaScript(ES2015,即 ES6),你可以在[这里](http://es6-features.org)查看新增的特性,网站做得很棒。 |
| 17 | +
|
| 18 | +### 参考材料 |
| 19 | + |
| 20 | +当你觉得有的概念不容易理解时,你可以在下面的链接里面寻找答案。 |
| 21 | + |
| 22 | +- [MDN (Mozilla Developer Network)](https://developer.mozilla.org/zh-CN/search?q=) |
| 23 | +- [You don't know JS(书)](https://github.com/getify/You-Dont-Know-JS) |
| 24 | +- [ES6 新特性和例子](http://es6-features.org) |
| 25 | +- [WesBos 博客中 ES6 类别](http://wesbos.com/category/es6/) |
| 26 | +- [Reddit (JavaScript)](https://www.reddit.com/r/javascript/) |
| 27 | +- [Google](https://www.google.com/) 可直接查找特定的博客和资源 |
| 28 | +- [StackOverflow](https://stackoverflow.com/questions/tagged/javascript) |
| 29 | + |
| 30 | +## 目录 |
| 31 | + |
| 32 | +- [Modern JavaScript Cheatsheet 简体中文版](#Modern-JavaScript-Cheatsheet-简体中文版) |
| 33 | + * [简介](#简介) |
| 34 | + + [初心](#初心) |
| 35 | + + [参考材料](#参考材料) |
| 36 | + * [目录](#目录) |
| 37 | + * [正文](#正文) |
| 38 | + + [变量声明: var, const, let](#变量声明-var-const-let) |
| 39 | + - [简述](#简述) |
| 40 | + - [代码示例](#代码示例) |
| 41 | + - [详述](#详述) |
| 42 | + - [延伸资料](#延伸资料) |
| 43 | + + [箭头函数](#箭头函数) |
| 44 | + - [简述](#简述) |
| 45 | + - [详述](#详述) |
| 46 | + * [简洁性](#简洁性) |
| 47 | + * [*this* 关键字](#this-关键字) |
| 48 | + - [延伸资料](#延伸资料) |
| 49 | + |
| 50 | +## 正文 |
| 51 | + |
| 52 | +### 变量声明: var, const, let |
| 53 | + |
| 54 | +在 JavaScript 中,声明变量时可以用三个不同的关键词,分别是 `var`,`let` 以及 `const` ,它们各有异同。 |
| 55 | + |
| 56 | +#### 简述 |
| 57 | + |
| 58 | +用 `const` 声明的变量,不能被重新赋值,而另两个 `var` 和 `let` 是可以的。 |
| 59 | + |
| 60 | +所以我建议默认情况下你都用 `const` 来声明变量,在你需要 *改变* 或是声明之后再重新指派它的时候,才用 `let` 来声明变量。 |
| 61 | + |
| 62 | + |
| 63 | +| - | 作用域 | 是否可重新赋值 | 是否可变 | [暂存死区](#tdz_sample) | |
| 64 | +| ----- | ---- | ------- | -------------------------- | ------------------- | |
| 65 | +| const | 块级 | × | [√](#const_mutable_sample) | √ | |
| 66 | +| let | 块级 | √ | √ | √ | |
| 67 | +| var | 函数 | √ | √ | × | |
| 68 | + |
| 69 | +#### 代码示例 |
| 70 | + |
| 71 | +```javascript |
| 72 | +const person = "Nick"; |
| 73 | +person = "John" // 因为 person 不能被重新赋值,所以会报错 |
| 74 | +``` |
| 75 | + |
| 76 | +```javascript |
| 77 | +let person = "Nick"; |
| 78 | +person = "John"; |
| 79 | +console.log(person) // "John", 使用 let 声明的变量可以重新赋值 |
| 80 | +``` |
| 81 | + |
| 82 | +#### 详述 |
| 83 | + |
| 84 | +简单来讲,变量的作用域([*scope*](#scope_def))是指“在这部分代码中可以访问到此变量”。 |
| 85 | + |
| 86 | +##### var |
| 87 | + |
| 88 | +使用 `var` 定义的变量,其作用域是定义它的函数内部(*function scoped*),也就是说在函数内部创建一个 `var` 变量的时候,在此函数内部可以任意访问这个变量,但在函数之外,这样的局部变量是无法被访问的。 |
| 89 | + |
| 90 | +我建议你这样理解,如果一个变量是 *X 作用域(scoped)* 类型的,那就是说这个变量是 X 的属性之一。(译注:X 有 function 和 block 两类,代表函数作用域和块级作用域。) |
| 91 | + |
| 92 | +```javascript |
| 93 | +function myFunction() { |
| 94 | + var myVar = "Nick"; |
| 95 | + console.log(myVar); // "Nick" - 在这个函数中 myVar 可被访问到 |
| 96 | +} |
| 97 | +console.log(myVar); // 抛出错误 ReferenceError, 在函数之外 myVar 则无法访问 |
| 98 | +``` |
| 99 | + |
| 100 | +继续来看变量的作用域,下面有更多精妙的例子: |
| 101 | + |
| 102 | +```javascript |
| 103 | +function myFunction() { |
| 104 | + var myVar = "Nick"; |
| 105 | + if (true) { |
| 106 | + var myVar = "John"; |
| 107 | + console.log(myVar); // "John" |
| 108 | + // 实际上 myVar 是函数级作用域变量,重新声明的时候,相当于用 "John" 抹去了 myVar 之前的值 "Nick" |
| 109 | + } |
| 110 | + console.log(myVar); // "John" - 可见 if 块中的代码会如何影响变量 |
| 111 | +} |
| 112 | +console.log(myVar); // 抛出错误 ReferenceError, 在函数之外 myVar 则无法访问 |
| 113 | +``` |
| 114 | + |
| 115 | +另外,*var* 声明的变量在执行的时候,就像会被移动到作用域的开始,这就是我们所说的[变量声明提升(var hoisting)](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/**var)。 |
| 116 | + |
| 117 | +所以看下面这段代码: |
| 118 | + |
| 119 | +```javascript |
| 120 | +console.log(myVar) // undefined -- 没有提示错误 |
| 121 | +var myVar = 2; |
| 122 | +``` |
| 123 | + |
| 124 | +之所以没有发生错误,是因为它执行时会被解释为这样: |
| 125 | + |
| 126 | +```javascript |
| 127 | +var myVar; |
| 128 | +console.log(myVar) // undefined -- 没有提示错误 |
| 129 | +myVar = 2; |
| 130 | +``` |
| 131 | + |
| 132 | +##### let |
| 133 | + |
| 134 | +`var` 和 `let` 几乎是一样的,但是用 `let` 声明的变量有如下特性: |
| 135 | + |
| 136 | +- *块级作用域*( block scoped ) |
| 137 | +- 在被赋值之前,是**无法**访问使用的 |
| 138 | +- 在同一个作用域之下,不能被重新声明 |
| 139 | + |
| 140 | +我们来看看之前例子中提到的块级作用域( block scoping )的效果: |
| 141 | + |
| 142 | +```javascript |
| 143 | +function myFunction() { |
| 144 | + let myVar = "Nick"; |
| 145 | + if (true) { |
| 146 | + let myVar = "John"; |
| 147 | + console.log(myVar); // "John" |
| 148 | + // 实际上 myVar 是块级作用域的变量,在 if 块中,我们相当于是创建了一个新变量, |
| 149 | + // 这个变量在此块之外是无法被访问的,而且它完全区别于我们创建的第一个 myVar 变量! |
| 150 | + } |
| 151 | + console.log(myVar); // "Nick", 可见 if 块中的代码,并没有影响到这个变量的值 |
| 152 | +} |
| 153 | +console.log(myVar); // 抛出错误 ReferenceError,在函数外部无法访问到 myVar。 |
| 154 | +``` |
| 155 | + |
| 156 | +<a name="tdz_sample"></a>现在,来看看 *let*(和 *const* )声明的变量在赋值前无法访问是什么意思: |
| 157 | + |
| 158 | +```javascript |
| 159 | +console.log(myVar) // 提示错误 ReferenceError ! |
| 160 | +let myVar = 2; |
| 161 | +``` |
| 162 | + |
| 163 | +这就是它们和 *var* 变量的区别,如果你在还未赋值给 *let* 或者 *const* 变量之前,就想读写它,是会提示错误的。这种情况常被称作暂存死区([*Temporal dead zone*](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/let#let_的暂存死区与错误))或者 *TDZ*。 |
| 164 | + |
| 165 | +> **注意:** 从技术上讲,*let* 和 *const* 变量声明时也存在提升,但并不代表它们的赋值也会被提升。但由于它被设计成了赋值之前无法使用,所以我们直观感觉上它没有被提升,但其实是存在提升的。如果想了解更多细节,请看[这篇文章](http://jsrocks.org/2015/01/temporal-dead-zone-tdz-demystified)。 |
| 166 | +
|
| 167 | + |
| 168 | +另外,在同一作用域内你不能重新声明一个 *let* 变量。 |
| 169 | + |
| 170 | +```js |
| 171 | +let myVar = 2; |
| 172 | +let myVar = 3; // 提示语法错误 SyntaxError |
| 173 | +``` |
| 174 | + |
| 175 | +##### const |
| 176 | + |
| 177 | +`const` 声明的变量很像 `let`,但它不能被重新赋值。 |
| 178 | + |
| 179 | +总结一下 `const` 变量的特点如下: |
| 180 | + |
| 181 | +- *块级作用域* |
| 182 | +- 赋值之前无法使用 |
| 183 | +- 在同一个作用域内部,你不能重新声明一个变量 |
| 184 | +- 不能被重新指派 |
| 185 | + |
| 186 | +```Javascript |
| 187 | +const myVar = "Nick"; |
| 188 | +myVar = "John" // 提示错误,不允许重新赋值 const 变量 |
| 189 | +``` |
| 190 | + |
| 191 | +<a name="const_mutable_sample"></a>但这里有一个小细节:`const` 变量并非完全[不可变](#mutation_def),如果这个变量是 `object` 和 `array` 类型的值,那它的值是**可以改变**的。assign |
| 192 | + |
| 193 | +对于对象类型来说: |
| 194 | + |
| 195 | +```js |
| 196 | +const person = { |
| 197 | + name: 'Nick' |
| 198 | +}; |
| 199 | +person.name = 'John' // 这会生效的!person 并非完全重新指派( reassigned ),只是值变化了( mutated ) |
| 200 | +console.log(person.name) // "John" |
| 201 | +person = "Sandra" // 提示错误,因为用 const 声明的变量不能被重新指派 |
| 202 | +``` |
| 203 | + |
| 204 | +对于数组类型来说: |
| 205 | + |
| 206 | +```js |
| 207 | +const person = []; |
| 208 | +person.push('John'); // 这也会生效!person 并非完全重新指派( reassigned ),只是值变化了( mutated ) |
| 209 | +console.log(person[0]) // "John" |
| 210 | +person = ["Nick"] // 提示错误,因为用 const 声明的变量不能被重新指派 |
| 211 | +``` |
| 212 | + |
| 213 | +#### 延伸资料 |
| 214 | + |
| 215 | +- [How let and const are scoped in JavaScript - WesBos](http://wesbos.com/javascript-scoping/) |
| 216 | +- [Temporal Dead Zone (TDZ) Demystified](http://jsrocks.org/2015/01/temporal-dead-zone-tdz-demystified) |
0 commit comments