var 和 let 的区别
One of the biggest problems with declaring variables with the var
keyword is that you can overwrite variable declarations without an error.
用var关键词去声明变量,是可以被覆盖的。
1 | var camper = 'James'; |
As you can see in the code above, the camper
variable is originally declared as James
and then overridden to be David
.
例如上面,原本变量camper是James,之后被David覆盖了。
In a small application, you might not run into this type of problem, but when your code becomes larger, you might accidentally overwrite a variable that you did not intend to overwrite.
在小的程序里,你可能不会遇到这种问题,但一旦你的代码多了起来,就可能覆盖了你原本没打算覆盖的变量。
Because this behavior does not throw an error, searching and fixing bugs becomes more difficult.
因为这不会造成异常,后期搜索及修复bugs的时候会变得很困难。
A new keyword called let
was introduced in ES6 to solve this potential issue with the var
keyword.
ES6里的关键词let可以解决这种问题。
If you were to replace var
with let
in the variable declarations of the code above, the result would be an error.
如果你用let去替换var,那结果是异常的。
1 | let camper = 'James'; |
This error can be seen in the console of your browser.
你在浏览器的控制台可以看到错误。
So unlike var
, when using let
, a variable with the same name can only be declared once.
当你使用let的时候,注意,相同名称的变量只能声明一次。
Note the "use strict"
. This enables Strict Mode, which catches common coding mistakes and “unsafe” actions. For instance:
请注意"use strict"
。这启用了严格模式,可以捕获常见的编码错误和“不安全”操作。
1 | ; |
练习
1 | let catName; |
对比 var 和 let 的作用域
When you declare a variable with the var
keyword, it is declared globally, or locally if declared inside a function.
当你用var声明变量时,声明是全局作用域或者在函数里的局部作用域。
The let
keyword behaves similarly, but with some extra features. When you declare a variable with the let
keyword inside a block, statement, or expression, its scope is limited to that block, statement, or expression.
关键词let也差不多,当你在block/statement/expression里用let声明变量,它的作用域就被限制在里面。
For example:
1 | var numArray = []; |
With the var
keyword, i
is declared globally. So when i++
is executed, it updates the global variable. This code is similar to the following:
用var关键词,i
是全局作用域,当执行到i++
时,会更新全局变量。
1 | var numArray = []; |
This behavior will cause problems if you were to create a function and store it for later use inside a for loop that uses the i
variable. This is because the stored function will always refer to the value of the updated global i
variable.
如果你要创建一个函数并将其存储以供以后在使用该i
变量的for循环中使用,则此行为将导致问题。这是因为存储的函数将始终引用更新的全局i
变量的值。
1 | var printNumTwo; |
As you can see, printNumTwo()
prints 3 and not 2. This is because the value assigned to i
was updated and the printNumTwo()
returns the global i
and not the value i
had when the function was created in the for loop. The let
keyword does not follow this behavior:
正如你所看到的,printNumTwo()
打印3而不是2.这是因为分配给的值i
已更新,并且printNumTwo()
返回全局i
而不是i
在for循环中创建函数时的值。该let
关键字不遵循这种行为:
1 | ; |
i
is not defined because it was not declared in the global scope. It is only declared within the for loop statement. printNumTwo()
returned the correct value because three different i
variables with unique values (0, 1, and 2) were created by the let
keyword within the loop statement.
i
未定义,因为它未在全局范围内声明。它仅在for循环语句中声明。printNumTwo()
返回正确的值,因为循环语句中i
的let
关键字创建了具有唯一值(0,1和2)的三个不同变量。
练习
1 | function checkScope() { |
Declare a Read-Only Variable with the const Keyword
let
is not the only new way to declare variables. In ES6, you can also declare variables using the const
keyword.
const
has all the awesome features that let
has, with the added bonus that variables declared using const
are read-only. They are a constant value, which means that once a variable is assigned with const
, it cannot be reassigned.
let
并不是唯一声明变量的新方法。在ES6中,还可以使用const
关键字声明变量。
const
的好处是声明使用的变量是只读的。它们是一个常量值,这意味着一旦赋值变量const
,就不能重新赋值。
1 |
|
As you can see, trying to reassign a variable declared with const
will throw an error. You should always name variables you don’t want to reassign using the const
keyword.
This helps when you accidentally attempt to reassign a variable that is meant to stay constant. A common practice when naming constants is to use all uppercase letters, with words separated by an underscore.
如你所见,尝试重新分配声明的变量const
将引发错误。你应该始终使用const
关键字命名您不想重新分配的变量。当你意外尝试重新分配一个旨在保持不变的变量时,这会有所帮助。
命名常量时的常见做法是使用全部大写字母,单词用下划线分隔。
练习
1 | function printManyTimes(str) { |
Mutate an Array Declared with const
The const
declaration has many use cases in modern JavaScript.
Some developers prefer to assign all their variables using const
by default, unless they know they will need to reassign the value. Only in that case, they use let
.
However, it is important to understand that objects (including arrays and functions) assigned to a variable using const
are still mutable. Using the const
declaration only prevents reassignment of the variable identifier.
const
声明在现代JavaScript中有许多用例。
一些开发人员更喜欢const
默认使用所有变量,除非他们知道需要重新分配值。只有在这种情况下,他们才会使用let
。
但是,重要的是要理解分配给变量的对象(包括数组和函数)const
仍然是可变的。使用const
声明仅阻止重新分配变量标识符。
1 | ; |
As you can see, you can mutate the object [5, 6, 7]
itself and the variable s
will still point to the altered array [5, 6, 45]
.
Like all arrays, the array elements in s
are mutable, but because const
was used, you cannot use the variable identifier s
to point to a different array using the assignment operator.
如你所见,你可以改变对象[5, 6, 7]
本身,变量s
仍将指向更改的数组[5, 6, 45]
。
与所有数组一样,数组元素s
是可变的,但由于const
被使用了,你不能使用变量标识符s
指向使用赋值运算符的不同的数组。
练习
1 | const s = [5, 7, 2]; |
Prevent Object Mutation
As seen in the previous challenge, const
declaration alone doesn’t really protect your data from mutation. To ensure your data doesn’t change, JavaScript provides a function Object.freeze
to prevent data mutation.
Once the object is frozen, you can no longer add, update, or delete properties from it. Any attempt at changing the object will be rejected without an error.
正如之前的挑战所示,const
仅凭声明并不能真正保护您的数据免受突变。为确保您的数据不会发生变化,JavaScript提供了Object.freeze
防止数据突变的功能。
对象冻结后,您将无法再从中添加,更新或删除属性。任何更改对象的尝试都将被拒绝而不会出现错误。
1 | let obj = { |
练习
1 | function freezeObj() { |
Use Arrow Functions to Write Concise Anonymous Functions
In JavaScript, we often don’t need to name our functions, especially when passing a function as an argument to another function. Instead, we create inline functions. We don’t need to name these functions because we do not reuse them anywhere else.
在JavaScript中,我们通常不需要命名我们的函数,特别是在将函数作为参数传递给另一个函数时。相反,我们创建内联函数。我们不需要命名这些函数,因为我们不会在其他任何地方重用它们。
To achieve this, we often use the following syntax:
为此,我们经常使用以下语法:
1 | const myFunc = function() { |
ES6 provides us with the syntactic sugar to not have to write anonymous functions this way. Instead, you can use arrow function syntax:
ES6为我们提供了语法糖,而不必以这种方式编写匿名函数。相反,你可以使用箭头函数语法:
1 | const myFunc = () => { |
When there is no function body, and only a return value, arrow function syntax allows you to omit the keyword return
as well as the brackets surrounding the code. This helps simplify smaller functions into one-line statements:
当没有函数体,并且只有返回值时,箭头函数语法允许你省略关键字return
以及代码周围的括号。这有助于将较小的函数简化为单行语句:
1 | const myFunc = () => "value" |
This code will still return value
by default.
练习
1 | const magic = () => { |
Write Arrow Functions with Parameters
Just like a normal function, you can pass arguments into arrow functions.
就像普通函数一样,您可以将参数传递给箭头函数。
1 | // doubles input value and returns it |
You can pass more than one argument into arrow functions as well.
你也可以将多个参数传递给箭头函数。
练习
1 | const myConcat = (arr1, arr2) => { |
Write Higher Order Arrow Functions
It’s time we see how powerful arrow functions are when processing data.
Arrow functions work really well with higher order functions, such as map()
, filter()
, and reduce()
, that take other functions as arguments for processing collections of data.
Read the following code:
是时候我们看到处理数据时箭头函数有多强大了。
箭头函数和其他高阶函数搭配一起时运作得很好,比如map()
,filter()
和reduce()
,是把其他的函数作为数据的处理收集论据。
阅读以下代码:
1 | FBPosts.filter(function(post) { |
We have written this with filter()
to at least make it somewhat readable. Now compare it to the following code which uses arrow function syntax instead:
我们写这篇文章filter()
至少使它有点可读。现在将它与以下使用箭头函数语法的代码进行比较:
1 | FBPosts.filter((post) => post.thumbnail !== null && post.shares > 100 && post.likes > 500) |
This code is more succinct and accomplishes the same task with fewer lines of code.
此代码更简洁,使用更少的代码行完成相同的任务。
练习
1 | const realNumberArray = [4, 5.6, -9.8, 3.14, 42, 6, 8.34]; |
Set Default Parameters for Your Functions
In order to help us create more flexible functions, ES6 introduces default parameters for functions.
Check out this code:
1 | function greeting(name = "Anonymous") { |
The default parameter kicks in when the argument is not specified (it is undefined). As you can see in the example above, the parameter name
will receive its default value "Anonymous"
when you do not provide a value for the parameter. You can add default values for as many parameters as you want.
练习
1 | const increment = (number, value = 1) => number + value; |
Use the Rest Operator with Function Parameters
In order to help us create more flexible functions, ES6 introduces the rest operator for function parameters. With the rest operator, you can create functions that take a variable number of arguments. These arguments are stored in an array that can be accessed later from inside the function.
Check out this code:
1 | function howMany(...args) { |
The rest operator eliminates the need to check the args
array and allows us to apply map()
, filter()
and reduce()
on the parameters array.
练习
1 | ; |
Use the Spread Operator to Evaluate Arrays In-Place
ES6 introduces the spread operator, which allows us to expand arrays and other expressions in places where multiple parameters or elements are expected.
The ES5 code below uses apply()
to compute the maximum value in an array:
1 | var arr = [6, 89, 3, 45]; |
We had to use Math.max.apply(null, arr)
because Math.max(arr)
returns NaN
. Math.max()
expects comma-separated arguments, but not an array.
The spread operator makes this syntax much better to read and maintain.
1 | const arr = [6, 89, 3, 45]; |
...arr
returns an unpacked array. In other words, it spreads the array.
However, the spread operator only works in-place, like in an argument to a function or in an array literal. The following code will not work:
1 | const spreaded = ...arr; // will throw a syntax error |
练习
1 | const arr1 = ['JAN', 'FEB', 'MAR', 'APR', 'MAY']; |
Use Destructuring Assignment to Assign Variables from Objects
We saw earlier how spread operator can effectively spread, or unpack, the contents of the array.
We can do something similar with objects as well. Destructuring assignment is special syntax for neatly assigning values taken directly from an object to variables.
Consider the following ES5 code:
1 | var voxel = {x: 3.6, y: 7.4, z: 6.54 }; |
Here’s the same assignment statement with ES6 destructuring syntax:
1 | const { x, y, z } = voxel; // x = 3.6, y = 7.4, z = 6.54 |
If instead you want to store the values of voxel.x
into a
, voxel.y
into b
, and voxel.z
into c
, you have that freedom as well.
1 | const { x : a, y : b, z : c } = voxel // a = 3.6, b = 7.4, c = 6.54 |
You may read it as “get the field x
and copy the value into a
,” and so on.
练习
1 | const AVG_TEMPERATURES = { |
Use Destructuring Assignment to Assign Variables from Nested Objects
We can similarly destructure nested objects into variables.
Consider the following code:
1 | const a = { |
In the example above, the variable start
is assigned the value of a.start
, which is also an object.
练习
1 | const LOCAL_FORECAST = { |
Use Destructuring Assignment to Assign Variables from Arrays
ES6 makes destructuring arrays as easy as destructuring objects.
One key difference between the spread operator and array destructuring is that the spread operator unpacks all contents of an array into a comma-separated list. Consequently, you cannot pick or choose which elements you want to assign to variables.
Destructuring an array lets us do exactly that:
1 | const [a, b] = [1, 2, 3, 4, 5, 6]; |
The variable a
is assigned the first value of the array, and b
is assigned the second value of the array.
We can also access the value at any index in an array with destructuring by using commas to reach the desired index:
1 | const [a, b,,, c] = [1, 2, 3, 4, 5, 6]; |
练习
1 | let a = 8, b = 6; |
Use Destructuring Assignment with the Rest Operator to Reassign Array Elements
In some situations involving array destructuring, we might want to collect the rest of the elements into a separate array.
The result is similar to Array.prototype.slice()
, as shown below:
1 | const [a, b, ...arr] = [1, 2, 3, 4, 5, 7]; |
Variables a
and b
take the first and second values from the array. After that, because of rest operator’s presence, arr
gets rest of the values in the form of an array.
The rest element only works correctly as the last variable in the list. As in, you cannot use the rest operator to catch a subarray that leaves out last element of the original array.
练习
1 | const source = [1,2,3,4,5,6,7,8,9,10]; |
Use Destructuring Assignment to Pass an Object as a Function’s Parameters
In some cases, you can destructure the object in a function argument itself.
Consider the code below:
1 | const profileUpdate = (profileData) => { |
This effectively destructures the object sent into the function. This can also be done in-place:
1 | const profileUpdate = ({ name, age, nationality, location }) => { |
This removes some extra lines and makes our code look neat.
This has the added benefit of not having to manipulate an entire object in a function; only the fields that are needed are copied inside the function.
练习
1 | const stats = { |
Create Strings using Template Literals
A new feature of ES6 is the template literal. This is a special type of string that makes creating complex strings easier.
Template literals allow you to create multi-line strings and to use string interpolation features to create strings.
Consider the code below:
1 | const person = { |
A lot of things happened there.
Firstly, the example uses backticks (1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Secondly, notice that the string is multi-line, both in the code and the output. This saves inserting `\n`within strings.
The `${variable}`syntax used above is a placeholder. Basically, you won't have to use concatenation with the `+`operator anymore. To add variables to strings, you just drop the variable in a template string and wrap it with `${`and `}`. Similarly, you can include other expressions in your string literal, for example `${a + b}`.
This new way of creating strings gives you more flexibility to create robust strings.
#### 练习
```javascript
const result = {
success: ["max-length", "no-amd", "prefer-arrow-functions"],
failure: ["no-var", "var-on-top", "linebreak"],
skipped: ["id-blacklist", "no-dup-keys"]
};
function makeList(arr) {
"use strict";
const resultDisplayArray = arr.map(val => `<li class="text-warning">${val}</li>`);
return resultDisplayArray;
}
/**
* makeList(result.failure) should return:
* [ `<li class="text-warning">no-var</li>`,
* `<li class="text-warning">var-on-top</li>`,
* `<li class="text-warning">linebreak</li>` ]
**/
const resultDisplayArray = makeList(result.failure);
Write Concise Object Literal Declarations Using Simple Fields
ES6 adds some nice support for easily defining object literals.
Consider the following code:
1 | const getMousePosition = (x, y) => ({ |
getMousePosition
is a simple function that returns an object containing two fields.
ES6 provides the syntactic sugar to eliminate the redundancy of having to write x: x
. You can simply write x
once, and it will be converted tox: x
(or something equivalent) under the hood.
Here is the same function from above rewritten to use this new syntax:
1 | const getMousePosition = (x, y) => ({ x, y }); |
练习
1 | const createPerson = (name, age, gender) => { |