原文地址:https://medium.freecodecamp.org/make-your-code-cleaner-shorter-and-easier-to-read-es6-tips-and-tricks-afd4ce25977c
更多内容请见译者 Blog:https://github.com/elevenbeans/elevenbeans.github.io
模版字符串
模板模版字符串使得字符串拼接比以前更容易。 它以反向标记(Tab 正上方那个字符)开始,并且可以使用${variable}
插入变量。 比较以下两行代码:
1 2 3
| var fName = 'Peter', sName = 'Smith', age = 43, job = 'photographer'; var a = 'Hi, I\'m ' + fName + ' ' + sName + ', I\'m ' + age + ' and work as a ' + job + '.'; var b = `Hi, I'm ${ fName } ${ sName }, I'm ${ age } and work as a ${ job }.`;
|
这会让一切变的更简单,代码更易于阅读。 你可以将任何内容放入花括号中:变量,方程式或函数调用。 我将在整篇文章的示例中使用它们。
块级作用域
JavaScript 本身一直是函数式作用域,这就是我们经常将整个 JavaScript 文件封装在一个空的立即调用函数表达式(IIFE)中的原因。这样做是为了隔离文件中的所有变量,因此全局中就不会存在变量名冲突。
现在,我们有块级作用域和两个绑定到块的新变量声明。
Let
这与var
类似,但有一些显着差异。由于它声明的变量具有块级作用域,所以可以在不影响外部变量的情况下声明具有相同名称的新局部(内部)变量。
1 2 3 4 5 6
| var a = 'car' ; { let a = 5; console.log(a) } console.log(a)
|
因为它绑定到一个块(即就是 {
和 }
之间的范围),它解决了这个经典的面试问题:“输出是什么,如何让它按照你的期望工作?”
1 2 3
| for (var i = 1; i < 5; i++){ setTimeout(() => { console.log(i); }, 1000); }
|
在这种情况下,它会输出 “5 5 5 5 5”,因为变量 i
在每次迭代中都会被改变,最终所有结果以最后一次变更为准。
如果您将var
切换为let
,那就不一样了。现在,每个循环都会创建一个新的块作用域,其值为绑定到该循环的值。相当于你写了:
1 2 3 4 5
| {let i = 1; setTimeout(() => { console.log(i) }, 1000)} {let i = 2; setTimeout(() => { console.log(i) }, 1000)} {let i = 3; setTimeout(() => { console.log(i) }, 1000)} {let i = 4; setTimeout(() => { console.log(i) }, 1000)} {let i = 5; setTimeout(() => { console.log(i) }, 1000)}
|
var
和let
之间的另一个区别是let
不会像var
一样被提升。
1 2 3 4 5 6
| { console.log(a); console.log(b); var a = 'car'; let b = 5; }
|
由于其更严格的范围界定和更可预测的行为,有些人表示应该使用let
而不是var
,除非特别需要用var
的声明提升或需要其更宽松的作用范围。
Const
如果你想在 JavaScript 中声明一个常量变量,那么惯例是将变量命名大写。然而,这并不能保证它是一个常量 - 它只是让其他开发人员知道这是一个常量,不应该改变。
现在我们有了const
声明。
1 2 3 4 5
| { const c = "tree"; console.log(c); c = 46; }
|
const
不会使变量不可变,只是锁定它的赋值。 如果你有一个复杂的赋值(对象或数组),那么该值仍然可以修改。
1 2 3 4 5 6 7 8
| { const d = [1, 2, 3, 4]; const dave = { name: 'David Jones', age: 32}; d.push(5); dave.job = "salesman"; console.log(d); console.log(dave); }
|
块级作用域函数问题
函数声明现在被指定为必须绑定到块级作用域。
1 2 3 4 5
| { bar(); function bar() { } } bar();
|
当你在if
语句中声明一个函数时,问题就来了。
考虑这个:
1 2 3 4 5 6
| if ( something) { function baz() { console.log('I passed') } } else { function baz() { console.log('I didn\'t pass') } } baz();
|
在 ES6 之前,这两个函数声明都会被提升,结果就是 ‘I didn\’t pass’,不管something
是什么东西。
现在我们会得到 ‘ReferenceError’,因为baz()
总是受到块范围的约束。
扩展运算符
ES6 引入了...
运算符,它被称为“扩展运算符”。 它有两个主要用途:将数组或对象分散到新的数组或对象中,并将多个参数合并到一个数组中。
第一个用例是你可能遇到的最多的用例,所以我们先看看。
1 2 3 4 5 6 7 8
| let a = [3, 4, 5]; let b = [1, 2, ...a, 6]; console.log(b); This can be very useful for passing in a set of variables to a function from an array. function foo(a, b, c) { console.log(`a=${a}, b=${b}, c=${c}`)} let data = [5, 15, 2]; foo( ...data);
|
也可以扩展对象,将每个键值对输入到新对象中。(实际上是在提案的第 4 阶段,并将在 ES2018 中正式推出,仅支持 Chrome 60 或更高版本,Firefox 55 或更高版本以及 Node 6.4.0 或更高版本)
1 2 3
| let car = { type: 'vehicle ', wheels: 4}; let fordGt = { make: 'Ford', ...car, model: 'GT'}; console.log(fordGt);
|
...
运算符的另一个特点是它创建一个新的数组或对象。 下面的例子为 b
创建了一个新的数组,但 c
只是指向同一个数组。
1 2 3 4 5 6 7 8 9
| let a = [1, 2, 3]; let b = [ ...a ]; let c = a; b.push(4); console.log(a); console.log(b); c.push(5); console.log(a); console.log(c);
|
第二个例子是将变量一起收集到一个数组中。 当你不知道有多少变量传递给函数时,这非常有用。
1 2 3 4
| function foo(...args) { console.log(args); } foo( 'car', 54, 'tree');
|
函数默认参数
现在可以使用默认参数定义函数。缺少或未定义的值将使用默认值进行初始化。只要小心 - 因为空值和假值会被强制为0。
1 2 3 4 5 6 7 8
| function foo( a = 5, b = 10) { console.log( a + b); } foo(); foo( 7, 12 ); foo( undefined, 8 ); foo( 8 ); foo( null );
|
默认值可以不仅仅是值 - 它们也可以是表达式或函数。
1 2 3 4 5 6 7
| function foo( a ) { return a * 4; } function bar( x = 2, y = x + 4, z = foo(x)) { console.log([ x, y, z ]); } bar(); bar( 1, 2, 3 ); bar( 10, undefined, 3 );
|
解构
解构是拆分等号左侧的数组或对象的过程。数组或对象可以来自变量,函数或等式。
1 2 3 4 5
| let [ a, b, c ] = [ 6, 2, 9]; console.log(`a=${a}, b=${b}, c=${c}`); function foo() { return ['car', 'dog', 6 ]; } let [ x, y, z ] = foo(); console.log(`x=${x}, y=${y}, z=${z}`);
|
通过对象解构,可以在大括号内列出对象的键以提取该键值对。
1 2 3 4 5
| function bar() { return {a: 1, b: 2, c: 3}; } let { a, c } = bar(); console.log(a); console.log(c); console.log(b);
|
有时,你想取值,将它们分配给一个新的变量。 这是通过在等号左边的 key: variable
配对完成的。
1 2 3 4 5 6 7 8 9 10 11
| function baz() { return { x: 'car', y: 'London', z: { name: 'John', age: 21} }; } let { x: vehicle, y: city, z: { name: driver } } = baz(); console.log( `I'm going to ${city} with ${driver} in their ${vehicle}.` );
|
对象解构允许的另一件事是为多个变量赋值。
1 2
| let { x: first, x: second } = { x: 4 }; console.log( first, second );
|
对象字面量和简明参数
当您从变量创建对象字面量时,ES6 允许您在与 key 与变量名称相同的情况下省略 key 名。
1 2 3 4
| let a = 4, b = 7; let c = { a: a, b: b }; let concise = { a, b }; console.log(c, concise)
|
这也可以与解构结合使用,使你的代码更简单,更清洁。
1 2 3 4 5 6 7 8 9 10 11 12
| function foo() { return { name: 'Anna', age: 56, job: { company: 'Tesco', title: 'Manager' } }; } let a = foo(), name = a.name, age = a.age, company = a.job.company; let { name, age, job: {company}} = foo();
|
它也可以用来解构对象传递给函数。方法 1 和 2 是你在 ES6 之前完成它的方法,方法 3 使用解构和简明的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| let person = { name: 'Anna', age: 56, job: { company: 'Tesco', title: 'Manager' } }; function old1(person) { var yearOfBirth = 2018 - person.age; console.log( `${ person.name } works at ${ person.job.company } and was born in ${ yearOfBirth }.`); } function old1(person) { var age = person.age, yearOfBirth = 2018 - age, name = person.name, company = person.job.company; console.log( `${ name } works at ${ company } and was born in ${ yearOfBirth }.`); } function es6({ age, name, job: {company}}) { var yearOfBirth = 2018 - age; console.log( `${ name } works at ${ company } and was born in ${ yearOfBirth }.`); }
|
使用 ES6,我们可以提取age
,name
和company
而无需额外的变量声明。
动态属性名称
ES6 添加了使用动态分配的 key 创建或添加属性的功能。
1 2 3 4 5 6
| let city= 'sheffield_'; let a = { [ city + 'population' ]: 350000 }; a[ city + 'county' ] = 'South Yorkshire'; console.log(a);
|
箭头函数
箭头函数有两个主要方面:结构和this
绑定。
它们可以具有比传统功能更简单的结构,因为它们不需要功能关键字,并且它们自动返回箭头之后的任何内容。
1 2 3 4
| var foo = function( a, b ) { return a * b; } let bar = ( a, b ) => a * b;
|
如果函数需要的不仅仅是一个简单的计算,可以使用大括号,并且该函数会返回花括号块范围返回的任何内容。
1 2 3 4 5
| let baz = ( c, d ) => { let length = c.length + d.toString().length; let e = c.join(', '); return `${e} and there is a total length of ${length}`; }
|
对于箭头函数最有用的地方之一是在map()
,forEach()
或sort()
之类的数组函数中。
1 2 3
| let arr = [ 5, 6, 7, 8, 'a' ]; let b = arr.map( item => item + 3 ); console.log(b);
|
除了具有更短的语法外,它还修复了在绑定行为经常出现的问题。具有 ES6 之前的功能的修复是将this
引用存储,通常作为自变量存储。
1 2 3 4 5 6 7 8 9 10
| var clickController = { doSomething: function (..) { var self = this; btn.addEventListener( 'click', function() { self.doSomething(..) }, False ); } };
|
这必须做到,因为这个绑定是动态的。 这意味着这个事件监听器里面的这个和doSomething
里面的这个不是指同一个事物。
在箭头函数里,这个绑定是词法的,而不是动态的。 这是箭头功能的主要设计特征。
虽然这种绑定可能是伟大的,有时这不是想要的。
1 2 3 4 5 6 7 8
| let a = { oneThing: ( a ) => { let b = a * 2; this.otherThing(b); }, otherThing: ( b ) => {....} }; a.oneThing(6);
|
当我们使用a.oneThing(6)
时,this.otherThing(b)
引用失败,因为这不是指向一个对象,而是指向周围的作用域。 如果您使用 ES6 语法重写遗留代码,则需要注意这一点。
for … of 循环
ES6 增加了一种迭代数组中每个值的方法。 这与现有的 ... in
不同,它循环使用key/index
。
1 2 3 4 5 6 7 8 9
| let a = ['a', 'b', 'c', 'd' ]; for ( var val of a ) { console.log( val ); } for ( var idx in a ) { console.log( idx ); }
|
使用新的for ... of
循环,不必在在每个循环内添加一个let val = a[idx]
。
数组,字符串,生成器和集合都可以在标准 JavaScript 中迭代。普通对象通常不能迭代,除非你已经为它定义了一个迭代器(Iterator)。
数字字面量
ES5 代码很好地处理了十进制和十六进制数字格式,但未指定八进制格式。事实上,它在严格的模式下被禁止。
ES6添加了一种新格式,在最初的 0 之后添加一个 o (注意是字母)以将该数字声明为八进制数。ES6 还添加了二进制格式。
1 2 3 4 5
| Number( 29 ) Number( 035 ) Number( 0o35 ) Number( 0x1d ) Number( 0b11101 )
|
还有很多。。。
ES6 为我们提供了更多的功能,使我们的代码更简洁,更短,更易于阅读和更强大。 我的目标是撰写本文的续篇,内容涵盖了 ES6 中不太知名的部分。