上次分享了es6開發環境的搭建,本次接着分享es6經常使用的特性。前端
1.變量聲明let和constes6
咱們都是知道在ES6之前,var關鍵字聲明變量。不管聲明在何處,都會被視爲聲明在函數的最頂部(不在函數內即在全局做用域的最頂部)。這就是函數變量提高例如:面試
function aa() { if(bool) { var test = 'hello man' } else { console.log(test) } }
以上的代碼其實是:編程
function aa() { var test // 變量提高 if(bool) { test = 'hello man' } else { //此處訪問test 值爲undefined console.log(test) } //此處訪問test 值爲undefined }
因此不用關心bool是否爲true or false。實際上,不管如何test都會被建立聲明。json
接下來ES6主角登場:
咱們一般用let和const來聲明,let表示變量、const表示常量。let和const都是塊級做用域。怎麼理解這個塊級做用域?api
說白了 {}大括號內的代碼塊即爲let 和 const的做用域。數組
看如下代碼:promise
function aa() { if(bool) { let test = 'hello man' } else { //test 在此處訪問不到 console.log(test) } }
let的做用域是在它所在當前代碼塊,但不會被提高到當前函數的最頂部。數據結構
再來講說const。閉包
const name = 'lux' name = 'joe' //再次賦值此時會報錯
說一道面試題
var funcs = [] for (var i = 0; i < 10; i++) { funcs.push(function() { console.log(i) }) } funcs.forEach(function(func) { func() })
這樣的面試題是你們常見,不少同窗一看就知道輸出 10 十次
可是若是咱們想依次輸出0到9呢?兩種解決方法。直接上代碼。
// ES5告訴咱們能夠利用閉包解決這個問題 var funcs = [] for (var i = 0; i < 10; i++) { func.push((function(value) { return function() { console.log(value) } }(i))) } // es6 for (let i = 0; i < 10; i++) { func.push(function() { console.log(i) }) }
達到相同的效果,es6簡潔的解決方案是否是更讓你心動!!!
es6模板字符簡直是開發者的福音啊,解決了ES5在字符串功能上的痛點。
第一個用途,基本的字符串格式化。將表達式嵌入字符串中進行拼接。用${}來界定。
//es5 var name = 'lux' console.log('hello' + name) //es6 const name = 'lux' console.log(`hello ${name}`) //hello lux
第二個用途,在ES5時咱們經過反斜槓(\)來作多行字符串或者字符串一行行拼接。ES6反引號(``)直接搞定。
// es5 var msg = "Hi \ man! " // es6 const template = `<div> <span>hello world</span> </div>`
對於字符串es6固然也提供了不少厲害的方法。說幾個經常使用的。
// 1.includes:判斷是否包含而後直接返回布爾值 let str = 'hahay' console.log(str.includes('y')) // true // 2.repeat: 獲取字符串重複n次 let s = 'he' console.log(s.repeat(3)) // 'hehehe' //若是你帶入小數, Math.floor(num) 來處理
函數默認參數
在ES5咱們給函數定義參數默認值是怎麼樣?
function action(num) { num = num || 200 //當傳入num時,num爲傳入的值 //當沒傳入參數時,num即有了默認值200 return num }
但細心觀察的同窗們確定會發現,num傳入爲0的時候就是false, 此時num = 200 與咱們的實際要的效果明顯不同
ES6爲參數提供了默認值。在定義函數時便初始化了這個參數,以便在參數沒有被傳遞進去時使用。
function action(num = 200) { console.log(num) } action() //200 action(300) //300
箭頭函數
ES6頗有意思的一部分就是函數的快捷寫法。也就是箭頭函數。
箭頭函數最直觀的三個特色。
//例如: [1,2,3].map( x => x + 1 ) //等同於: [1,2,3].map((function(x){ return x + 1 }).bind(this))
說個小細節。
當你的函數有且僅有一個參數的時候,是能夠省略掉括號的。當你函數返回有且僅有一個表達式的時候能夠省略{};例如:
var people = name => 'hello' + name //參數name就沒有括號
做爲參考
var people = (name, age) => { const fullName = 'h' + name return fullName } //若是缺乏()或者{}就會報錯
對象初始化簡寫
ES5咱們對於對象都是以鍵值對的形式書寫,是有可能出現鍵值對重名的。例如:
function people(name, age) { return { name: name, age: age }; }
鍵值對重名,ES6能夠簡寫以下:
function people(name, age) { return { name, age }; }
ES6 一樣改進了爲對象字面量方法賦值的語法。ES5爲對象添加方法:
const people = { name: 'lux', getName: function() { console.log(this.name) } }
ES6經過省略冒號與 function 關鍵字,將這個語法變得更簡潔
const people = { name: 'lux', getName () { console.log(this.name) } }
ES6 對象提供了Object.assign()這個方法來實現淺複製。Object.assign()能夠把任意多個源對象自身可枚舉的屬性拷貝給目標對象,而後返回目標對象。第一參數即爲目標對象。在實際項目中,咱們爲了避免改變源對象。通常會把目標對象傳爲{}
const obj = Object.assign({}, objA, objB)
數組和對象是JS中最經常使用也是最重要表示形式。爲了簡化提取信息,ES6新增瞭解構,這是將一個數據結構分解爲更小的部分的過程
ES5咱們提取對象中的信息形式以下:
const people = { name: 'lux', age: 20 } const name = people.name const age = people.age console.log(name + ' --- ' + age)
是否是以爲很熟悉,沒錯,在ES6以前咱們就是這樣獲取對象信息的,一個一個獲取。如今,解構能讓咱們從對象或者數組裏取出數據存爲變量,例如
//對象 const people = { name: 'lux', age: 20 } const { name, age } = people console.log(`${name} --- ${age}`) //數組 const color = ['red', 'blue'] const [first, second] = color console.log(first) //'red' console.log(second) //'blue'
ES6中另一個好玩的特性就是Spread Operator 也是三個點兒...接下來就展現一下它的用途。
組裝對象或者數組
//數組 const color = ['red', 'yellow'] const colorful = [...color, 'green', 'pink'] console.log(colorful) //[red, yellow, green, pink] //對象 const alp = { fist: 'a', second: 'b'} const alphabets = { ...alp, third: 'c' } console.log(alphabets) //{ "fist": "a", "second": "b", "third": "c" }
有時候咱們想獲取數組或者對象除了前幾項或者除了某幾項的其餘項
const number = [1,2,3,4,5] const [first, ...rest] = number console.log(rest) //2,3,4,5 //對象 const user = { username: 'lux', gender: 'female', age: 19, address: 'peking' } const { username, ...rest } = user console.log(rest) //{"address": "peking", "age": 19, "gender": "female" }
對於 Object 而言,還能夠用於組合成新的 Object 。(ES2017 stage-2 proposal) 固然若是有重複的屬性名,右邊覆蓋左邊
const first = { a: 1, b: 2, c: 6, } const second = { c: 3, d: 4 } const total = { ...first, ...second } console.log(total) // { a: 1, b: 2, c: 3, d: 4 }
import導入模塊、export導出模塊
//所有導入 import people from './example' //有一種特殊狀況,即容許你將整個模塊看成單一對象進行導入 //該模塊的全部導出都會做爲對象的屬性存在 import * as example from "./example.js" console.log(example.name) console.log(example.age) console.log(example.getName()) //導入部分 import {name, age} from './example' // 導出默認, 有且只有一個默認 export default App // 部分導出 export class App extend Component {};
之前有人問我,導入的時候有沒有大括號的區別是什麼。下面是我在工做中的總結:
1.當用export default people導出時,就用 import people 導入(不帶大括號)
2.一個文件裏,有且只能有一個export default。但能夠有多個export。
3.當用export name 時,就用import { name }導入(記得帶上大括號)
4.當一個文件裏,既有一個export default people, 又有多個export name 或者 export age時,導入就用 import people, { name, age }
5.當一個文件裏出現n多個 export 導出不少模塊,導入時除了一個一個導入,也能夠用import * as example
在promise以前代碼過多的回調或者嵌套,可讀性差、耦合度高、擴展性低。經過Promise機制,扁平化的代碼機構,大大提升了代碼可讀性;用同步編程的方式來編寫異步代碼,保存線性的代碼邏輯,極大的下降了代碼耦合性而提升了程序的可擴展性。
說白了就是用同步的方式去寫異步代碼。
發起異步請求
fetch('/api/todos') .then(res => res.json()) .then(data => ({ data })) .catch(err => ({ err }));
今天看到一篇關於面試題的頗有意思。
setTimeout(function() { console.log(1) }, 0); new Promise(function executor(resolve) { console.log(2); for( var i=0 ; i<10000 ; i++ ) { i == 9999 && resolve(); } console.log(3); }).then(function() { console.log(4); }); console.log(5);
固然以上promise的知識點,這個只是冰山一角。須要更多地去學習應用。
生成器( generator)是能返回一個迭代器的函數。生成器函數也是一種函數,最直觀的表現就是比普通的function多了個星號*,在其函數體內可使用yield關鍵字,有意思的是函數會在每一個yield後暫停。
這裏生活中有一個比較形象的例子。我們到銀行辦理業務時候都得向大廳的機器取一張排隊號。你拿到你的排隊號,機器並不會自動爲你再出下一張票。也就是說取票機「暫停」住了,直到下一我的再次喚起纔會繼續吐票。
OK。說說迭代器。當你調用一個generator時,它將返回一個迭代器對象。這個迭代器對象擁有一個叫作next的方法來幫助你重啓generator函數並獲得下一個值。next方法不只返回值,它返回的對象具備兩個屬性:done和value。value是你得到的值,done用來代表你的generator是否已經中止提供值。繼續用剛剛取票的例子,每張排隊號就是這裏的value,打印票的紙是否用完就這是這裏的done。
// 生成器 function *createIterator() { yield 1; yield 2; yield 3; } // 生成器能像正規函數那樣被調用,但會返回一個迭代器 let iterator = createIterator(); console.log(iterator.next().value); // 1 console.log(iterator.next().value); // 2 console.log(iterator.next().value); // 3
那生成器和迭代器又有什麼用處呢?
圍繞着生成器的許多興奮點都與異步編程直接相關。異步調用對於咱們來講是很困難的事,咱們的函數並不會等待異步調用完再執行,你可能會想到用回調函數,(固然還有其餘方案好比Promise好比Async/await)。
生成器可讓咱們的代碼進行等待。就不用嵌套的回調函數。使用generator能夠確保當異步調用在咱們的generator函數運行一下行代碼以前完成時暫停函數的執行。
那麼問題來了,我們也不能手動一直調用next()方法,你須要一個可以調用生成器並啓動迭代器的方法。就像這樣子的
function run(taskDef) { //taskDef即一個生成器函數 // 建立迭代器,讓它在別處可用 let task = taskDef(); // 啓動任務 let result = task.next(); // 遞歸使用函數來保持對 next() 的調用 function step() { // 若是還有更多要作的 if (!result.done) { result = task.next(); step(); } } // 開始處理過程 step(); }
生成器與迭代器最有趣、最使人激動的方面,或許就是可建立外觀清晰的異步操做代碼。你沒必要處處使用回調函數,而是能夠創建貌似同步的代碼,但實際上卻使用 yield 來等待異步操做結束。
ES6的特性遠不止於此,但對於咱們平常的開發開說。這已是夠夠的了。還有不少有意思的方法。好比findIndex...等等。包括用set來完成面試題常客數組去重問題。我和個人小夥伴們都驚呆了!