FCC ES6

var 和 let 的区别

One of the biggest problems with declaring variables with the varkeyword is that you can overwrite variable declarations without an error.

用var关键词去声明变量,是可以被覆盖的。

1
2
3
4
var camper = 'James';
var camper = 'David';
console.log(camper);
// logs 'David'

As you can see in the code above, the campervariable is originally declared as Jamesand 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 letwas introduced in ES6 to solve this potential issue with the varkeyword.

ES6里的关键词let可以解决这种问题。

If you were to replace varwith letin the variable declarations of the code above, the result would be an error.

如果你用let去替换var,那结果是异常的。

1
2
let camper = 'James';
let camper = 'David'; // throws an error

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
2
"use strict";
x = 3.14; // throws an error because x is not declared

练习

1
2
3
4
5
6
7
8
9
10
let catName;
let quote;
function catTalk() {
"use strict";

catName = "Oliver";
quote = catName + " says Meow!";

}
catTalk();

对比 var 和 let 的作用域

When you declare a variable with the varkeyword, it is declared globally, or locally if declared inside a function.

当你用var声明变量时,声明是全局作用域或者在函数里的局部作用域。

The letkeyword behaves similarly, but with some extra features. When you declare a variable with the letkeyword inside a block, statement, or expression, its scope is limited to that block, statement, or expression.

关键词let也差不多,当你在block/statement/expression里用let声明变量,它的作用域就被限制在里面。

For example:

1
2
3
4
5
6
7
8
var numArray = [];
for (var i = 0; i < 3; i++) {
numArray.push(i);
}
console.log(numArray);
// returns [0, 1, 2]
console.log(i);
// returns 3

With the varkeyword, iis declared globally. So when i++is executed, it updates the global variable. This code is similar to the following:

用var关键词,i是全局作用域,当执行到i++时,会更新全局变量。

1
2
3
4
5
6
7
8
9
var numArray = [];
var i;
for (i = 0; i < 3; i++) {
numArray.push(i);
}
console.log(numArray);
// returns [0, 1, 2]
console.log(i);
// returns 3

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 ivariable. This is because the stored function will always refer to the value of the updated global ivariable.

如果你要创建一个函数并将其存储以供以后在使用该i变量的for循环中使用,则此行为将导致问题。这是因为存储的函数将始终引用更新的全局i变量的值。

1
2
3
4
5
6
7
8
9
10
var printNumTwo;
for (var i = 0; i < 3; i++) {
if(i === 2){
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo());
// returns 3

As you can see, printNumTwo()prints 3 and not 2. This is because the value assigned to iwas updated and the printNumTwo()returns the global iand not the value ihad when the function was created in the for loop. The letkeyword does not follow this behavior:

正如你所看到的,printNumTwo()打印3而不是2.这是因为分配给的值i已更新,并且printNumTwo()返回全局i而不是i在for循环中创建函数时的值。该let关键字不遵循这种行为:

1
2
3
4
5
6
7
8
9
10
11
12
13
'use strict';
let printNumTwo;
for (let i = 0; i < 3; i++) {
if (i === 2) {
printNumTwo = function() {
return i;
};
}
}
console.log(printNumTwo());
// returns 2
console.log(i);
// returns "i is not defined"

iis 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 ivariables with unique values (0, 1, and 2) were created by the letkeyword within the loop statement.

i未定义,因为它未在全局范围内声明。它仅在for循环语句中声明。printNumTwo()返回正确的值,因为循环语句中ilet关键字创建了具有唯一值(0,1和2)的三个不同变量。

练习

1
2
3
4
5
6
7
8
9
10
11
function checkScope() {
'use strict';
let i = 'function scope';
if (true) {
let i = 'block scope';
console.log('Block scope i is: ', i);
}

console.log('Function scope i is: ', i);
return i;
}

Declare a Read-Only Variable with the const Keyword

letis not the only new way to declare variables. In ES6, you can also declare variables using the constkeyword.

consthas all the awesome features that lethas, with the added bonus that variables declared using constare 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
2
3
"use strict"
const FAV_PET = "Cats";
FAV_PET = "Dogs"; // returns error

As you can see, trying to reassign a variable declared with constwill throw an error. You should always name variables you don’t want to reassign using the constkeyword.

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
2
3
4
5
6
7
8
9
10
11
12
13
14
function printManyTimes(str) {
"use strict";

// change code below this line

const SENTENCE = str + " is cool!";
for(let i = 0; i < str.length; i+=2) {
console.log(SENTENCE);
}

// change code above this line

}
printManyTimes("freeCodeCamp");

Mutate an Array Declared with const

The constdeclaration has many use cases in modern JavaScript.

Some developers prefer to assign all their variables using constby 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 constare still mutable. Using the constdeclaration only prevents reassignment of the variable identifier.

const声明在现代JavaScript中有许多用例。

一些开发人员更喜欢const默认使用所有变量,除非他们知道需要重新分配值。只有在这种情况下,他们才会使用let

但是,重要的是要理解分配给变量的对象(包括数组和函数)const仍然是可变的。使用const声明仅阻止重新分配变量标识符。

1
2
3
4
5
"use strict";
const s = [5, 6, 7];
s = [1, 2, 3]; // throws error, trying to assign a const
s[2] = 45; // works just as it would with an array declared with var or let
console.log(s); // returns [5, 6, 45]

As you can see, you can mutate the object [5, 6, 7]itself and the variable swill still point to the altered array [5, 6, 45].

Like all arrays, the array elements in sare mutable, but because constwas used, you cannot use the variable identifier sto point to a different array using the assignment operator.

如你所见,你可以改变对象[5, 6, 7]本身,变量s仍将指向更改的数组[5, 6, 45]

与所有数组一样,数组元素s是可变的,但由于const被使用了,你不能使用变量标识符s指向使用赋值运算符的不同的数组。

练习

1
2
3
4
5
6
7
8
9
10
11
12
const s = [5, 7, 2];
function editInPlace() {
'use strict';
// change code below this line

// s = [2, 5, 7]; <- this is invalid
s[0] = 2;
s[1] = 5;
s[2] = 7;
// change code above this line
}
editInPlace();

Prevent Object Mutation

As seen in the previous challenge, constdeclaration alone doesn’t really protect your data from mutation. To ensure your data doesn’t change, JavaScript provides a function Object.freezeto 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
2
3
4
5
6
7
8
9
let obj = {
name:"FreeCodeCamp",
review:"Awesome"
};
Object.freeze(obj);
obj.review = "bad"; //will be ignored. Mutation not allowed
obj.newProp = "Test"; // will be ignored. Mutation not allowed
console.log(obj);
// { name: "FreeCodeCamp", review:"Awesome"}

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function freezeObj() {
'use strict';
const MATH_CONSTANTS = {
PI: 3.14
};
// change code below this line
Object.freeze(MATH_CONSTANTS);

// change code above this line
try {
MATH_CONSTANTS.PI = 99;
} catch(ex) {
console.log(ex);
}
return MATH_CONSTANTS.PI;
}
const PI = 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
2
3
4
const myFunc = function() {
const myVar = "value";
return myVar;
}

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
2
3
4
const myFunc = () => {
const myVar = "value";
return myVar;
}

When there is no function body, and only a return value, arrow function syntax allows you to omit the keyword returnas 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 valueby default.

练习

1
2
3
4
const magic = () => {
"use strict";
return new Date();
};

Write Arrow Functions with Parameters

Just like a normal function, you can pass arguments into arrow functions.

就像普通函数一样,您可以将参数传递给箭头函数。

1
2
// doubles input value and returns it
const doubler = (item) => item * 2;

You can pass more than one argument into arrow functions as well.

你也可以将多个参数传递给箭头函数。

练习

1
2
3
4
5
6
const myConcat = (arr1, arr2) =>  {
"use strict";
return arr1.concat(arr2);
};
// test your code
console.log(myConcat([1, 2], [3, 4, 5]));

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
2
3
FBPosts.filter(function(post) {
return post.thumbnail !== null && post.shares > 100 && post.likes > 500;
})

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const realNumberArray = [4, 5.6, -9.8, 3.14, 42, 6, 8.34];
const squareList = (arr) => {
"use strict";
// change code below this line
const squaredIntegers = [];
arr.map( num => {
if(Number.isInteger(num)){
squaredIntegers.push(num * num);
}
})
// change code above this line
return squaredIntegers;
};
// test your code
const squaredIntegers = squareList(realNumberArray);
console.log(squaredIntegers);

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
2
3
4
5
function greeting(name = "Anonymous") {
return "Hello " + name;
}
console.log(greeting("John")); // Hello John
console.log(greeting()); // Hello 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 namewill 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
2
3
4
const increment = (number, value = 1) => number + value;

console.log(increment(5, 2)); // returns 7
console.log(increment(5)); // returns 6

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
2
3
4
5
function howMany(...args) {
return "You have passed " + args.length + " arguments.";
}
console.log(howMany(0, 1, 2)); // You have passed 3 arguments
console.log(howMany("string", null, [1, 2, 3], { })); // You have passed 4 arguments.

The rest operator eliminates the need to check the argsarray and allows us to apply map(), filter()and reduce()on the parameters array.

练习

1
2
3
4
5
'use strict';
function sum(...args) {
return args.reduce((a, b) => a + b, 0);
}
console.log(sum(1, 2, 3)); // 6

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
2
var arr = [6, 89, 3, 45];
var maximus = Math.max.apply(null, arr); // returns 89

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
2
const arr = [6, 89, 3, 45];
const maximus = Math.max(...arr); // returns 89

...arrreturns 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
2
3
4
const arr1 = ['JAN', 'FEB', 'MAR', 'APR', 'MAY'];
let arr2;

arr2 = [...arr1];

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
2
3
4
var voxel = {x: 3.6, y: 7.4, z: 6.54 };
var x = voxel.x; // x = 3.6
var y = voxel.y; // y = 7.4
var z = voxel.z; // 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.xinto a, voxel.yinto b, and voxel.zinto 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 xand copy the value into a,” and so on.

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const AVG_TEMPERATURES = {
today: 77.5,
tomorrow: 79
};

function getTempOfTmrw(avgTemperatures) {
"use strict";
// change code below this line
const {tomorrow:tempOfTomorrow} = avgTemperatures; // change this line
// change code above this line
return tempOfTomorrow;
}

console.log(getTempOfTmrw(AVG_TEMPERATURES)); // should be 79

Use Destructuring Assignment to Assign Variables from Nested Objects

We can similarly destructure nested objects into variables.

Consider the following code:

1
2
3
4
5
6
const a = {
start: { x: 5, y: 6},
end: { x: 6, y: -9 }
};
const { start : { x: startX, y: startY }} = a;
console.log(startX, startY); // 5, 6

In the example above, the variable startis assigned the value of a.start, which is also an object.

练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const LOCAL_FORECAST = {
today: { min: 72, max: 83 },
tomorrow: { min: 73.3, max: 84.6 }
};

function getMaxOfTmrw(forecast) {
"use strict";
// change code below this line
const {tomorrow:{max:maxOfTomorrow}} = forecast; // change this line
// change code above this line
return maxOfTomorrow;
}

console.log(getMaxOfTmrw(LOCAL_FORECAST)); // should be 84.6

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
2
const [a, b] = [1, 2, 3, 4, 5, 6];
console.log(a, b); // 1, 2

The variable ais assigned the first value of the array, and bis 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
2
const [a, b,,, c] = [1, 2, 3, 4, 5, 6];
console.log(a, b, c); // 1, 2, 5

练习

1
2
3
4
5
6
7
8
9
let a = 8, b = 6;
(() => {
"use strict";
// change code below this line
[a,b] = [b,a];
// change code above this line
})();
console.log(a); // should be 6
console.log(b); // should be 8

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
2
3
const [a, b, ...arr] = [1, 2, 3, 4, 5, 7];
console.log(a, b); // 1, 2
console.log(arr); // [3, 4, 5, 7]

Variables aand btake the first and second values from the array. After that, because of rest operator’s presence, arrgets 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
2
3
4
5
6
7
8
9
10
11
const source = [1,2,3,4,5,6,7,8,9,10];
function removeFirstTwo(list) {
"use strict";
// change code below this line
const [a,b,...arr] =list // change this
// change code above this line
return arr;
}
const arr = removeFirstTwo(source);
console.log(arr); // should be [3,4,5,6,7,8,9,10]
console.log(source); // should be [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
2
3
4
const profileUpdate = (profileData) => {
const { name, age, nationality, location } = profileData;
// do something with these variables
}

This effectively destructures the object sent into the function. This can also be done in-place:

1
2
3
const profileUpdate = ({ name, age, nationality, location }) => {
/* do something with these fields */
}

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
2
3
4
5
6
7
8
9
10
11
12
13
const stats = {
max: 56.78,
standard_deviation: 4.34,
median: 34.54,
mode: 23.87,
min: -0.75,
average: 35.85
};
// change code below this line
const half = ({max, min}) => ((max + min) / 2.0); // use function argument destructurung
// change code above this line
console.log(stats); // should be object
console.log(half(stats)); // should be 28.015

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
2
3
4
5
6
7
8
9
10
11
12
const person = {
name: "Zodiac Hasbro",
age: 56
};

// Template literal with multi-line and string interpolation
const greeting = `Hello, my name is ${person.name}!
I am ${person.age} years old.`;

console.log(greeting); // prints
// Hello, my name is Zodiac Hasbro!
// I am 56 years old.

A lot of things happened there.

Firstly, the example uses backticks (

not quotes (`'`or `"`), to wrap the string.
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
2
3
4
const getMousePosition = (x, y) => ({
x: x,
y: y
});

getMousePositionis 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 xonce, 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
2
3
4
5
6
7
8
const createPerson = (name, age, gender) => {
"use strict";
return {
name,
age,
gender
};
};