這篇文章翻譯自ES6 tips and tricks to make your code cleaner, shorter, and easier to read!. 文章就代碼整潔方面對es6進行了總結。若有錯誤歡迎指出。html
模板字符串使字符串的使用變得比之前更簡單了,他們以反引號開始(`),而且能過使用${變量}
來插入變量。咱們來比較一下下面兩行代碼。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限制在當前的循環,他能夠理解爲這樣:ecmascript
{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
同樣被變量提高函數
{ 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'} }
函數的聲明也能夠限制在塊級做用域中。
{ 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
一直被限定在塊級做用域內。
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}
擴展運算符的另外一個特色是,他能夠生成一個<mark>新的數組或者對象</mark>. 下面的這個例子,就是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' ]
函數如今可使用默認的參數值來定義了。不傳參或者未定義值都被初始化爲默認值。可是須要注意的是,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 ]
解構是拆開等號左邊的數組或者對象的過程。這個數組或者對象能夠來自一個變量,一個函數,或者一個等式
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,咱們能提取出age
,name
,和 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的語法,這個是你須要注意的事情。
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來迭代,除非你本身定義一個迭代器。
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,或者看一下這個超讚的小網站