[翻译] 让你的代码更简短,更整洁,更易读的ES6小技巧

让你的代码更简短,更整洁,更易读的ES6小技巧

写在文章前面

这篇文章翻译自ES6 tips and tricks to make your code cleaner, shorter, and easier to read!. 文章就代码整洁方面对es6进行了总结。若有错误欢迎指出。html

template literals 模板字符串

模板字符串使字符串的使用变得比之前更简单了,他们以反引号开始(`),而且能过使用${变量}来插入变量。咱们来比较一下下面两行代码。node

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 }.`;
复制代码

一切都变得很美好了是否是,代码更易读了是否是?你能够在大括号内放入任何东西:变量,等式,或者函数的调用。 我将会在后面的整个文章的示例中使用这些方式。git

块级做用域语法

JavaScript是使用函数做用域的,这就是为何咱们是为何咱们愈来愈频繁的使用匿名的当即执行函数表达式(iife)来实现整个JavaScript文件的封装。咱们这么作是为了把全部的变量隔离在文件内从而避免变量冲突。es6

如今咱们有了块级做用域和两个崭新的块级做用域的变量声明github

let declaration let命令

这个命令和var很类似但却又有着显著的不一样。由于他是有块级做用域的,声明一个相同名字的新变量能够彻底不影响外部的变量。面试

var a = 'car' ;
{
    let a = 5;
    console.log(a) // 5
}
console.log(a) // car
复制代码

由于他是被限制在块级做用域的,他解决了那道很是经典的面试题:“下面这个代码的输出是什么,如何修改让他运行以后成为你想的那个样子?”chrome

for (var i = 1; i < 5; i++){
    setTimeout(() => { console.log(i); }, 1000);
}
复制代码

这个例子中,输出是“5 5 5 5 5”由于变量i在每次迭代中都会改变。数组

若是咱们把var变为let,一切都变了。 如今,每次循环都会建立一个全新的块级做用域吧i限制在当前的循环,他能够理解为这样:bash

{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)} 
复制代码

varlet的另一个区别是 let 不会像 var同样被变量提高ecmascript

{ 
    console.log(a); // undefined
    console.log(b); // ReferenceError
    var a = 'car';
    let b = 5;
}
复制代码

由于他有更为局限的做用域,以及更能被预测的行为,所以一些人甚至认为你应该使用let来代替var, 除非当你真的特别须要变量提高或者更宽松的做用域范围,你再使用var

Const

在之前,若是你想在JavaScript中声明一个常量, 习惯性的作法是使用全大写来命名。然鹅,这不是真的去保护了这个变量不能被更改---只是让其余的开发者知道,这是一个常量,它不该该被更改。

如今咱们有了const命令.

const没有让变量彻底不可变,只是锁定他的赋值,当你有一个复杂的变量(数组或者对象)的时候,值仍是能够被修改的。

{
    const d = [1, 2, 3, 4];
    const dave = { name: 'David Jones', age: 32};
    d.push(5); 
    dave.job = "salesman";
    console.log(d);  // [1, 2, 3, 4, 5]
    console.log(dave);  // { age: 32, job: "salesman", name: 'David Jones'}
}
复制代码

Problem with block scoping functions函数块级做用域化带来的问题

函数的声明也能够限制在块级做用域中。

{
    bar(); // works
    function bar() { /* do something */ }
}
bar();  // doesn't work 复制代码

可是当你在一个if语句中声明一个函数的时候问题来了。

想一下这种状况:

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一直被限定在块级做用域内。

Spread 扩展运算符

ES6介绍了...操做符,这个操做符指的就是‘扩展运算符‘。他的主要用途有两个:1. 将一个数组或者对象放到一个新的数组或者对象中 2. 将数组中的多个参数合并在一块儿

第一个用途多是你将会使用的最多的。因此咱们先来看他。

let a = [3, 4, 5];
let b = [1, 2, ...a, 6];
console.log(b);  // [1, 2, 3, 4, 5, 6]
复制代码

若是咱们想把一个数组内的一组参数传递给函数,这个时候扩展运算符就十分的有用了。

function foo(a, b, c) { 
console.log(`a=${a}, b=${b}, c=${c}`)
} 
let data = [5, 15, 2];
foo( ...data); // a=5, b=15, c=2
复制代码

一个对象也能够扩展的,它会把每一个键值对写入新的对象中。( 对象扩展已经在提议的第四阶段,并且将会在es2018中正式出现 。但这种特性目前只被chrome60及之后的版本,Firefox55及之后,node 6.4.0及之后的版本所支持)【译者注:在2ality博客中的es2018一文中得知,在刚刚结束的TC39会议中,ECMA2018的特性被敲定了。】

let car = { type: 'vehicle ', wheels: 4};
let fordGt = { make: 'Ford', ...car, model: 'GT'};
console.log(fordGt); // {make: 'Ford', model: 'GT', type: 'vehicle', wheels: 4}
复制代码

扩展运算符的另外一个特色是,他能够生成一个新的数组或者对象. 下面的这个例子,就是b就是新建的数组,但c只是引用同一个数组。

let a = [1, 2, 3];
let b = [ ...a ];
let c = a;
b.push(4);
console.log(a);  // [1, 2, 3]
console.log(b);  // [1, 2, 3, 4] 不一样的数组
c.push(5);
console.log(a);  // [1, 2, 3, 5] 
console.log(c);  // [1, 2, 3, 5] 同一个数组
复制代码

第二个用法是把变量汇集到一个数组里面。当你不知道一个函数到底有多少的传参的时候会这个方法会变得很是的有用。

function foo(...args) {
    console.log(args); 
} 
foo( 'car', 54, 'tree');  //  [ 'car', 54, 'tree' ] 
复制代码

Default Parameter 参数默认值

函数如今可使用默认的参数值来定义了。不传参或者未定义值都被初始化为默认值。可是须要注意的是,null和false都会被强转为0.

function foo( a = 5, b = 10) {
    console.log( a + b);
} 
foo();  // 15
foo( 7, 12 );  // 19
foo( undefined, 8 ); // 13
foo( 8 ); // 18
foo( null ); // 10 as null is coerced to 0
复制代码

默认值的类型能够不只仅是值类型---还能够是表达式或者函数。

function foo( a ) { return a * 4; }
function bar( x = 2, y = x + 4, z = foo(x)) {
    console.log([ x, y, z ]);
}
bar();  // [ 2, 6, 8 ]
bar( 1, 2, 3 ); //[ 1, 2, 3 ] 
bar( 10, undefined, 3 );  // [ 10, 14, 3 ]
复制代码

Destructuring解构

解构是拆开等号左边的数组或者对象的过程。这个数组或者对象能够来自一个变量,一个函数,或者一个等式

let [ a, b, c ] = [ 6, 2, 9];
console.log(`a=${a}, b=${b}, c=${c}`); //a=6, b=2, c=9

function foo() { return ['car', 'dog', 6 ]; } 
let [ x, y, z ] = foo();
console.log(`x=${x}, y=${y}, z=${z}`);  // x=car, y=dog, z=6
复制代码

对象类型的结构,能够在花括号内列出对象的键来提取键值对。

function bar() { return {a: 1, b: 2, c: 3}; }
let { a, c } = bar();
console.log(a); // 1
console.log(c); // 3
console.log(b); // undefined
复制代码

有时,你可能想提取出值而后费赔给新的变量,这个能够经过在等号左侧使用一个“key:variable”(键:变量名)来完成。

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}.` ); // I'm going to London with John in their car. 
复制代码

此外,对象的结构容许给多个变量赋值。

let { x: first, x: second } = { x: 4 };
console.log( first, second ); // 4, 4
复制代码

对象字面量和属性的简洁表达法

当你从许多参数建立对象字面量的时候,ES6容许你在键与变量名字相同的状况下省略该键。

let a = 4, b = 7;
let c = { a: a, b: b };
let concise = { a, b };
console.log(c, concise) // {a: 4, b: 7}, {a: 4, b: 7}
复制代码

这个还能够与解构一块儿用来使你的代码更干净整洁。

function foo() {
    return {
        name: 'Anna', 
        age: 56,
       job: { company: 'Tesco', title: 'Manager' }
    };
} 
// pre ES6
let a = foo(), name = a.name, age = a.age, company = a.job.company;
// ES6 destructuring and concise parameters 
let { name, age, job: {company}} = foo();

复制代码

简洁表示法还能够用于解构对象并把它传入函数。方法1和2是你在es6以前要怎么作, 方法三是使用解构和简洁表达法。

let person = {
    name: 'Anna', 
    age: 56,
    job: { company: 'Tesco', title: 'Manager' }
};
// method 1
function old1( person) {
    var yearOfBirth = 2018 - person.age;
    console.log( `${ person.name } works at ${ person.job.company } and was born in ${ yearOfBirth }.`);
}
// method 2
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 }.`);
} 
// method 3
function es6({ age, name, job: {company}}) {
    var yearOfBirth = 2018 - age;
    console.log( `${ name } works at ${ company } and was born in ${ yearOfBirth }.`);
} 

复制代码

经过使用ES6,咱们能提取出agename,和 company,而不须要任何其余的变量声明。

动态属性名称

ES6添加了使用动态分配的键建立或添加属性的功能。

let  city= 'sheffield_';
let a = {
    [ city + 'population' ]: 350000
};
a[ city + 'county' ] = 'South Yorkshire';
console.log(a); // {sheffield_population: 350000, sheffield_county: 'South Yorkshire' }

复制代码

箭头函数

箭头函数有两个比较重要的特色: 他们的结构以及他们的this 指向

他们比传统的函数有更简单的结构由于他们不须要关键字function 并且他们能够自动返回在箭头后面的一部分,不管箭头后面的是什么。

var foo = function( a, b ) {
    return a * b;
} 
let bar = ( a, b ) => a * b;
复制代码

若是函数有多于一个的计算式,可使用花括号来包起来,而后函数返回块做用域返回的任何内容。

箭头函数一个最重要的用途之一就是应用在数组的相关函数中,像.map.forEach,.sort等等。

let arr = [ 5, 6, 7, 8, 'a' ];
let b = arr.map( item => item + 3 );
console.log(b); // [ 8, 9, 10, 11, 'a3' ]
复制代码

在拥有一个更短的表达方式的同时,箭头函数还修复了有关于this绑定行为常常出现的问题。ES6以前解决这个问题一般是使用一个self变量来存储这个指向。

var clickController = {
    doSomething: function (..) {
        var self = this;
        btn.addEventListener(
            'click', 
            function() { self.doSomething(..) }, 
            False
       );
   } 
};
复制代码

这个this的赋值是必定要作的,由于this的绑定是动态的。这就意味着this在eventlistener内部和在doSomething内部指的并非同一个东西。

在箭头函数内部,this的绑定是语义上的就是指当前的,而不是动态的。这也是箭头函数的主要设计特色。

虽然这种词法上的this很棒,可是有些时候,他却不是咱们想要的那样。

let a = {
    oneThing: ( a ) => {
         let b = a * 2;
         this.otherThing(b);
    }, 
    otherThing: ( b ) => {....} 
};
a.oneThing(6);
复制代码

当咱们使用a.oneThing(6), 这个this.otherThing(6) 会抛出引用失败的错误,由于this没有指向对象a,而是指向了环境做用域。若是你正在使用ES6的代码使用ES6的语法,这个是你须要注意的事情。

for...of loops (for...of循环)

ES6新添加了一种方式来迭代数组中的每一个值,这个方式是与已经存在的for...in的经过索引的循环方式不一样。

let a = ['a', 'b', 'c', 'd' ];
// ES6 
for ( var val of a ) {
    console.log( val );
} // "a" "b" "c" "d"
// pre-ES6 
for ( var idx in a ) {
    console.log( idx );
}  // 0 1 2 3
复制代码

使用新的for ... of循环,在每一个循环内部保存了一个let val = a[idx]

数组,字符串,generator以及从collection 在标准JavaScript中都是可迭代的。普通的对象没法正常的使用for...of来迭代,除非你本身定义一个迭代器。

Number Literals 数字字面量

ES5代码很好处理了十进制和十六进制的数字格式,但并未指定八进制的格式。实际上,八进制在严格模式中是被禁止使用的。

ES6 添加了一个全新的格式,在最开始的0后面添加一个o来声明一个八进制的数。与此同时,在es6中还添加了二进制格式。

Number( 29 )  // 29
Number( 035 ) // 35 in old octal form. 
Number( 0o35 ) // 29 in new octal form 
Number( 0x1d ) // 29 in hexadecimal 
Number( 0b11101 ) // 29 in binary form
复制代码

更多

ES6还提供了咱们不少不少其余的方式来使咱们的代码更简洁,更易读,以及更稳定。个人目标时写一篇这篇文章的延续,来包括一些ES6中不太知名的部分。

若是你已经等不及了,能够读一读Kyle Simpson的YOU DONT KNOW JAVASCRIPT ON ES6,或者看一下这个超赞的小网站

相关文章
相关标签/搜索