本篇文章是根據如下內容進行的總結javascript
一、https://segmentfault.com/a/1190000011344301html
二、http://www.bslxx.com/a/mianshiti/tiku/2017/1019/953.htmljava
三、http://www.bslxx.com/a/mianshiti/tiku/javascript/2017/1213/1505.htmlnode
前言jquery
自從ES6發佈以來,就受到了廣大開發者的歡迎。它的新特性解決了不少實際開發中的痛點,而且使得JavaScript逐步成爲一門可以開發大型企業應用的編程語言,基於這種技術環境下,不少公司都將ES6視爲開發的其中一個標準,所以在招聘人才的時候,也會對其進行ES6知識的考察。下面就來看看哪些ES6知識是咱們須要重點掌握的。es6
箭頭函數須要注意的地方面試
*當要求動態上下文的時候,就不可以使用箭頭函數,也就是this的固定化。ajax
一、在使用=>定義函數的時候,this的指向是定義時所在的對象,而不是使用時所在的對象;
二、不可以用做構造函數,這就是說,不可以使用new命令,不然就會拋出一個錯誤;
三、不可以使用arguments對象;
四、不能使用yield命令;編程
下面來看一道面試題,重點說明下第一個知識點:json
class Animal { constructor() { this.type = "animal"; } say(val) { setTimeout(function () { console.log(this); //window console.log(this.type + " says " + val); }, 1000) } } var animal = new Animal(); animal.say("hi"); //undefined says hi
【拓展】
《JavaScript高級程序設計》第二版中,寫到:「超時調用的代碼都是在全局做用域中執行的,所以函數中this的值在非嚴格模式下指向window對象,在嚴格模式下是undefined」。也就是說在非嚴格模式下,setTimeout中所執行函數中的this,永遠指向window!!
咱們再來看看箭頭函數(=>)的狀況:
class Animal { constructor() { this.type = "animal"; } say(val) { setTimeout(() => { console.log(this); //Animal console.log(this.type + ' says ' + val); }, 1000) } } var animal = new Animal(); animal.say("hi"); //animal says hi
【特色】
let和const
*let是更完美的var,不是全局變量,具備塊級函數做用域,大多數狀況不會發生變量提高。const定義常量值,不可以從新賦值,若是值是一個對象,能夠改變對象裏邊的屬性值。
一、let聲明的變量具備塊級做用域
二、let聲明的變量不能經過window.變量名進行訪問
三、形如for(let x..)的循環是每次迭代都爲x建立新的綁定
下面是var帶來的不合理場景
var arr = []; for (var i = 0; i < 10; i++) { arr[i] = function () { console.log(i); } } arr[5]() //10,a[5]輸出f(){console.log(i);},後面加個括號表明執行f()
在上述代碼中,變量i是var聲明的,在全局範圍類都有效,因此用來計數的循環變量泄露爲全局變量。因此每一次循環,新的i值都會覆蓋舊值,致使最後輸出都是10。
而若是對循環使用let語句的狀況,那麼每次迭代都是爲x建立新的綁定代碼以下:
var arr = []; for (let i = 0; i < 10; i++) { arr[i] = function () { console.log(i); } } arr[5]() //5,a[5]輸出f(){console.log(i);},後面加個括號表明執行f()
【拓展】
固然,除了這種方式讓數組找中的各個元素分別是不一樣的函數,咱們還能夠採用ES5中的閉包和當即函數兩種方法。
一、採用閉包
function showNum(i) { return function () { console.log(i) } } var a = [] for (var i = 0; i < 5; i++) { a[i] = showNum(i)(); //循環輸出1,2,3,4 }
二、採用當即執行函數
var a = [] for (var i = 0; i < 5; i++) { a[i] = (function (i) { return function () { console.log(i) } })(i) } a[2](); //2
【面試】
把如下代碼使用兩種方法,依次輸出0-9
var funcs = [] for (var i = 0; i < 10; i++) { funcs.push(function () { console.log(i) }) } funcs.forEach(function (func) { func(); //輸出十個10 })
方法一:使用當即執行函數
var funcs = [] for (var i = 0; i < 10; i++) { funcs.push((function (value) { return function () { console.log(value) } }(i))) } funcs.forEach(function (func) { func(); //依次輸出0-9 })
方法二:使用閉包
function show(i) { return function () { console.log(i) } } var funcs = [] for (var i = 0; i < 10; i++) { funcs.push(show(i)) } funcs.forEach(function (func) { func(); //0 1 2 3 4 5 6 7 8 9 })
方法三:使用let
var funcs = [] for (let i = 0; i < 10; i++) { funcs.push(function () { console.log(i) }) } funcs.forEach(function (func) { func(); //依次輸出0-9 })
其餘知識點:forEach() 方法用於調用數組的每一個元素,並將元素傳遞給回調函數。戳這裏查看參考文章
Set數據結構
*es6方法,Set自己是一個構造函數,它相似於數組,可是成員值都是惟一的。
const set = new Set([1,2,3,4,4]) console.log([...set] )// [1,2,3,4] console.log(Array.from(new Set([2,3,3,5,6]))); //[2,3,5,6]
Class的講解
*class語法相對原型、構造函數、繼承更接近傳統語法,它的寫法可以讓對象原型的寫法更加清晰、面向對象編程的語法更加通俗
這是class的具體用法。
class Animal { constructor() { this.type = 'animal' } says(say) { console.log(this.type + 'says' + say) } } let animal = new Animal() animal.says('hello') // animal says hello class Cat extends Animal { constructor() { super() this.type = 'cat' } } let cat = new Cat() cat.says('hello') // cat says hell
能夠看出在使用extend的時候結構輸出是cat says hello 而不是animal says hello。說明contructor內部定義的方法和屬性是實例對象本身的,不能經過extends 進行繼承。在class cat中出現了super(),這是什麼呢?由於在ES6中,子類的構造函數必須含有super函數,super表示的是調用父類的構造函數,雖然是父類的構造函數,可是this指向的倒是cat。
更詳細的參考文章
模板字符串
*就是這種形式${varible}
,在以往的時候咱們在鏈接字符串和變量的時候須要使用這種方式'string' + varible + 'string'可是有了模版語言後咱們可使用string${varible}string
這種進行鏈接。基本用途有以下:
一、基本的字符串格式化,將表達式嵌入字符串中進行拼接,用${}來界定。
//es5 var name = 'lux'; console.log('hello' + name); //es6 const name = 'lux'; console.log(`hello ${name}`); //hello lux
二、在ES5時咱們經過反斜槓(\)來作多行字符串或者字符串一行行拼接,ES6反引號(``)直接搞定。
//ES5 var template = "hello \ world"; console.log(template); //hello world //ES6 const template = `hello world`; console.log(template); //hello 空行 world
【拓展】
字符串的其餘方法
// 1.includes:判斷是否包含而後直接返回布爾值 let str = 'hahay' console.log(str.includes('y')) // true // 2.repeat: 獲取字符串重複n次 let s = 'he' console.log(s.repeat(3)) // 'hehehe'
重點「人物」:Promise!
概念:Promise是異步編程的一種解決方案,比傳統的解決方案(回調函數和事件)更合合理、強大。所謂Promise,簡單來講就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promies是一個對象,從它能夠獲取異步操做的消息。Promise提供統一的API,各類異步操做均可以用一樣的方法進行處理。處理過程流程圖:
【面試套路1】
手寫一個promise
var promise = new Promise((resolve, reject) => { if (操做成功) { resolve(value) } else { reject(error) } }) promise.then(function (value) { // success }, function (value) { // failure })
【面試套路2】
怎麼解決回調函數裏面回調另外一個函數,另外一個函數的參數須要依賴這個回調函數。須要被解決的代碼以下:
$http.get(url).success(function (res) { if (success != undefined) { success(res); } }).error(function (res) { if (error != undefined) { error(res); } }); function success(data) { if( data.id != 0) { var url = "getdata/data?id=" + data.id + ""; $http.get(url).success(function (res) { showData(res); }).error(function (res) { if (error != undefined) { error(res); } }); } }
【面試套路3】
如下代碼依次輸出的內容是?
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);
上述代碼解析:
首先先碰到一個 setTimeout,因而會先設置一個定時,在定時結束後將傳遞這個函數放到任務隊列裏面,所以開始確定不會輸出 1 。 而後是一個 Promise,裏面的函數是直接執行的,所以應該直接輸出 2 3 。 而後,Promise 的 then 應當會放到當前 tick 的最後,可是仍是在當前 tick 中。 所以,應當先輸出 5,而後再輸出 4 , 最後在到下一個 tick,就是 1 。
【面試套路4】
jQuery的ajax返回的是promise對象嗎?
jquery的ajax返回的是deferred對象,經過promise的resolve()方法將其轉換爲promise對象。 var jsPromise = Promise.resolve($.ajax('/whatever.json'));
【面試套路5】
promise只有2個狀態,成功和失敗,怎麼讓一個函數不管成功仍是失敗都能被調用?
使用promise.all() Promise.all方法用於將多個Promise實例,包裝成一個新的Promise實例。 Promise.all方法接受一個數組做爲參數,數組裏的元素都是Promise對象的實例,若是不是,就會先調用下面講到的Promise.resolve方法,將參數轉爲Promise實例,再進一步處理。(Promise.all方法的參數能夠不是數組,但必須具備Iterator接口,且返回的每一個成員都是Promise實例。) 示例: var p =Promise.all([p1,p2,p3]); p的狀態由p一、p二、p3決定,分爲兩種狀況。 當該數組裏的全部Promise實例都進入Fulfilled狀態:Promise.all**返回的實例纔會變成Fulfilled狀態。並將Promise實例數組的全部返回值組成一個數組,傳遞給Promise.all返回實例的回調函數**。 當該數組裏的某個Promise實例都進入Rejected狀態:Promise.all返回的實例會當即變成Rejected狀態。並將第一個rejected的實例返回值傳遞給Promise.all返回實例的回調函數。
【面試套路6】
1、分析下列程序代碼,得出運行結果,解釋其緣由
const promise = new Promise((resolve, reject) => { console.log(1) resolve() console.log(2) }) promise.then(() => { console.log(3) }) console.log(4)
運行結果及緣由
運行結果: 1 2 4 3 緣由: Promise 構造函數是同步執行的,promise.then 中的函數是異步執行的。
2、分析下列程序代碼,得出運行結果,解釋其緣由
const promise1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('success') }, 1000) }) const promise2 = promise1.then(() => { throw new Error('error!!!') }) console.log('promise1', promise1) console.log('promise2', promise2) setTimeout(() => { console.log('promise1', promise1) console.log('promise2', promise2) }, 2000)
運行結果及緣由
運行結果: promise1 Promise { <pending> } promise2 Promise { <pending> } (node:50928) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: error!!! (node:50928) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code. promise1 Promise { 'success' } promise2 Promise { <rejected> Error: error!!! at promise.then (...) at <anonymous> } 緣由: promise 有 3 種狀態:pending(進行中)、fulfilled(已完成,又稱爲Resolved) 或 rejected(已失敗)。狀態改變只能是 pending->fulfilled 或者 pending->rejected,狀態一旦改變則不能再變。上面 promise2 並非 promise1,而是返回的一個新的 Promise 實例。
3、分析下列程序代碼,得出運行結果,解釋其緣由
const promise = new Promise((resolve, reject) => { resolve('success1') reject('error') resolve('success2') }) promise .then((res) => { console.log('then: ', res) }) .catch((err) => { console.log('catch: ', err) })
運行結果及緣由
運行結果:
then:success1
緣由:
構造函數中的 resolve 或 reject 只有第一次執行有效,屢次調用沒有任何做用,呼應代碼二結論:promise 狀態一旦改變則不能再變。
4、分析下列程序代碼,得出運行結果,解釋其緣由
Promise.resolve(1) .then((res) => { console.log(res) return 2 }) .catch((err) => { return 3 }) .then((res) => { console.log(res) })
運行結果及緣由
運行結果: 1 2 緣由: promise 能夠鏈式調用。提起鏈式調用咱們一般會想到經過 return this 實現,不過 Promise 並非這樣實現的。promise 每次調用 .then 或者 .catch 都會返回一個新的 promise,從而實現了鏈式調用。
5、分析下列程序代碼,得出運行結果,解釋其緣由
const promise = new Promise((resolve, reject) => { setTimeout(() => { console.log('once') resolve('success') }, 1000) }) const start = Date.now() promise.then((res) => { console.log(res, Date.now() - start) }) promise.then((res) => { console.log(res, Date.now() - start) })
運行結果及緣由
運行結果: once success 1001 success 1001 緣由: promise 的 .then 或者 .catch 能夠被調用屢次,但這裏 Promise 構造函數只執行一次。或者說 promise 內部狀態一經改變,而且有了一個值,那麼後續每次調用 .then 或者 .catch 都會直接拿到該值。
6、分析下列程序代碼,得出運行結果,解釋其緣由
Promise.resolve() .then(() => { return new Error('error!!!') }) .then((res) => { console.log('then: ', res) }) .catch((err) => { console.log('catch: ', err) })
運行結果及緣由
運行結果 then: Error: error!!! at Promise.resolve.then (...) at ... 緣由 .then 或者 .catch 中 return 一個 error 對象並不會拋出錯誤,因此不會被後續的 .catch 捕獲,須要改爲其中一種: return Promise.reject(new Error('error!!!')) throw new Error('error!!!') 由於返回任意一個非 promise 的值都會被包裹成 promise 對象,即 return new Error('error!!!') 等價於 return Promise.resolve(new Error('error!!!'))。
7、分析下列程序代碼,得出運行結果,解釋其緣由
const promise = Promise.resolve() .then(() => { return promise }) promise.catch(console.error)
運行結果及緣由
運行結果 TypeError: Chaining cycle detected for promise #<Promise> at <anonymous> at process._tickCallback (internal/process/next_tick.js:188:7) at Function.Module.runMain (module.js:667:11) at startup (bootstrap_node.js:187:16) at bootstrap_node.js:607:3 緣由 .then 或 .catch 返回的值不能是 promise 自己,不然會形成死循環。
8、分析下列程序代碼,得出運行結果,解釋其緣由
Promise.resolve(1) .then(2) .then(Promise.resolve(3)) .then(console.log)
運行結果及緣由
運行結果 1 緣由 .then 或者 .catch 的參數指望是函數,傳入非函數則會發生值穿透。
9、分析下列程序代碼,得出運行結果,解釋其緣由
Promise.resolve() .then(function success (res) { throw new Error('error') }, function fail1 (e) { console.error('fail1: ', e) }) .catch(function fail2 (e) { console.error('fail2: ', e) })
運行結果及緣由
運行結果 fail2: Error: error at success (...) at ... 緣由 .then 能夠接收兩個參數,第一個是處理成功的函數,第二個是處理錯誤的函數。.catch 是 .then 第二個參數的簡便寫法,可是它們用法上有一點須要注意:.then 的第二個處理錯誤的函數捕獲不了第一個處理成功的函數拋出的錯誤,然後續的 .catch 能夠捕獲以前的錯誤。
10、分析下列程序代碼,得出運行結果,解釋其緣由
process.nextTick(() => { console.log('nextTick') }) Promise.resolve() .then(() => { console.log('then') }) setImmediate(() => { console.log('setImmediate') }) console.log('end')
運行結果及緣由
運行結果
end
nextTick
then
setImmediate
緣由
process.nextTick 和 promise.then 都屬於 microtask,而 setImmediate 屬於 macrotask,在事件循環的 check 階段執行。事件循環的每一個階段(macrotask)之間都會執行 microtask,事件循環的開始會先執行一次 microtask。