es6常用功能與非同步詳解(JS高階面試題)
callback hell方法的使用
可讀性不友好
function loadImg(src,callback,fail){ var img = document.createElement('img'); img.onload = function(){ callback(img); } img.onerror = function(){ fail(); } img.src = src; } var src = "https://www.baidu.com/img/baidu_jgylogo3.gif"; loadImg(src,function(img){ console.log('img width',img.width) },function(){ console.log('faild') })
用promise 去改造
用多個.then去分批處理
function loadImg(src){ return new Promise(function(resolve,reject){ var img = document.createElement('img'); img.onload = function(){ resolve(img); } img.onerror = function(){ reject(); } img.src = src; }) } var src="https://www.baidu.com/img/baidu_jgylogo3.gif"; var result = loadImg(src); result.then(function(img){ console.log('img width',img.width) },function(){ console.log('faild') }) result.then(function(img){ console.log('img height',img.height) })
問題解答:
- new promise例項,而且要傳入return
- new promise時要傳入函式,函式有resolve reject函式
- 成功時執行resolve()失敗時執行reject()
- .then監聽結果
ES6常用功能
- let 定義變數,可以重新賦值
- const 定義常量,不可以重新賦值
- 多行字串/模板變數
- 解構賦值(詳情見es6)
- 塊級作用域(很大的改動)
- 函式預設引數
- 箭頭函式
塊級作用域
<!-- 在js裡面 --> var obj = {a:100,b:200} for (var key in obj) { console.log(key,'for in') } console.log(key)// 100,200 <!-- 在es6 --> var obj = {a:100,b:200} for (let key in obj) { console.log(key,'for in') } console.log(key); // undefine
箭頭函式改變this指向的問題
<!-- 用箭頭函式,指向本身函式物件 --> function fn(){ console.log('real',this); // {a:100} var arr = [1,2,3]; arr.map(item=>{ console.log(this); // {a:100} }) } fn.call({a:100}); // call是強制讓fn函式物件指向{a:100} <!-- 不用箭頭函式,this指向window --> function fn(){ console.log('real',this); // {a:100} var arr = [1,2,3]; arr.map(function(item){ console.log(this); // window; }) } fn.call({a:100});
原型
描述一下zepto是如何使用原型
描述一下jquery如何使用原型
再結合自己的專案經驗,說一個自己開發的列子
$p.css('color','red'); // css是原型方法 $p.html(); // html是原型方法
原型如何體現他的擴充套件性
非同步
問題:
一. 什麼是單執行緒,和非同步有什麼關係
- 單執行緒只有一個執行緒,同一時間只能做一件事情
var i,sum = 0; for(i=0;i<100000;i++){ sum+=i; } console.log(sum); <!-- alert不處理,js執行和dom渲染暫時卡頓 --> console.log(1); alert('hello'); console.log(2);
-
用單執行緒的原因-避免DOM渲染的衝突
A. 瀏覽器需要渲染DOM
B. js可以修改DOM結構
C. JS執行的時候,瀏覽器DOM渲染會暫停
D. 兩段JS也不能同時執行(都修改DOM就衝突了)
E. webworder支援多執行緒,但是不能訪問DOM
-
單執行緒的解決單執行緒方案-非同步
console.log(100) setTimeout(function(){ console.log(200); },1000) console.log(300); console.log(400); // 100 // 300 // 400 // 200一秒後執行
console.log(100) $.ajax({ url:'XXXX', success:function(res){ console.log(res) // 最後執行 } }); console.log(300) cobsole.log(400)
非同步的問題:
問題一:沒有按照書寫的方式執行,可讀性差
問題二:callback中不容易模組化(setTimeout和ajax裡面的函式)
二. 什麼是event-loop 非同步的解決方案。
- 事件輪詢,js實現非同步的具體解決方案
- 同步程式碼,直接執行
- 非同步函式先放在非同步佇列中;有setTimeout等待時間過後,才放入非同步佇列中。ajax執行完畢過後放入非同步佇列
- 待同步函式執行完畢,輪詢執行非同步佇列的函式
回答點:
- 什麼是非同步佇列,何時被放入非同步佇列
- 輪詢的過程。
三. 是否用過jquery的deferred
問題解答:
1. 可以拿jquery1.5 對ajax的改變舉例 2. 說明如何簡單的封裝、使用deferred,說出它的好處 3. 說明promise和deferred的區別
jQuery1.5的變化——1.5之前
var ajax = $.ajax({ url:'./data.json', success:function(){ console.log('success 1'); console.log('success 2'); console.log('success 3'); }, error:function(){ console.log('error'); } })
jQuery1.5的變化——1.5之後
這樣寫的好處:
- 這樣對修改封閉,對擴充套件開放,
- 增加一個函式,不用再修改原來的函式
- 多人開發,一人維護一個函式。
- 測試也只測試新的函式。
- 對程式碼管理和維護有好處的
// 每一個方法都會執行 var ajax = $.ajax('data.json'); ajax.done(function(){ console.log('success 1'); }).fail(function(){ console.log('fail 1'); }).done(function(){ console.log('success 2'); }).fail(function(){ console.log('fail 2'); }).done(function(){ console.log('success 3'); }).fail(function(){ console.log('fail 3'); })
<!-- 與promise很相近了 --> var ajax = $.ajax('data.json') ajax.then(function(){ console.log('success 1'); },function(){ console.log('error 1'); }).then(function(){ console.log('success 2'); },function(){ console.log('error 2'); })
- 無法改變js非同步和單執行緒的本質
- 只能從寫法上杜絕callback這種形式
- 它是一種語法糖形式,但是解耦了程式碼
- 很好的體現:開放封閉原則
- 減少了迴歸測試的成本。多人開發方便。
使用jquery deferred舉例:
var wait = function(){ var task = function(){ console.log('執行完成'); } setTimeout(task,20000) } wait();
function waitHandle(){ // 定義 var dtd = $.Deferred(); var wait = function(dtd){ var task = function(){ console.log('執行完畢'); // 成功 dtd.resolve() // 失敗 // dtd.reject() } setTimeout(task,2000); // wait返回 return dtd; } // 最終返回 return wait(dtd); } var w = waitHandle(); w.then(function(){ console.log('ok 1'); },function(){ console.log('err 1'); }) w.then(function(){ console.log('ok 2'); },function(){ console.log('err 2'); }) w.then(function(){ console.log('ok 3'); },function(){ console.log('err 3'); }) w.reject();//不會報錯 // 開放封閉原則!!!
總結:
dtd的API可分成兩類,用意不同
第一類:dtd.reolvedtd.reject主動觸發 第二類:dtd.thendtd.donedtd.fail被動監聽
初步引入promise概念,是 jquery deferred 引入過來的
promise和jquery deferred的區別:
promise物件只能被動監聽,不能主動修改,所以在w.reject()會報錯。
function waitHandle(){ // 定義 var dtd = $.Deferred(); var wait = function(dtd){ var task = function(){ console.log('執行完畢'); // 成功 dtd.resolve() // 失敗 // dtd.reject() } setTimeout(task,2000); // wait返回 return dtd.promise(); // 這裡返回的是promise物件 } // 最終返回 return wait(dtd); } var w = waitHandle(); $.when(w).then(function(){ console.log('ok 1'); },function(){ console.log('err 1'); }).then(function(){ console.log('ok 2'); },function(){ console.log('err 2'); }) w.reject();// 這裡就會直接報錯。
四. promise的基本使用和原理
基本語法回顧
1. 異常捕獲
function loadImg(src){ return new Promise(resolve,reject){ var img = document.createElement('img'); img.onload = function(){ resolve(img); } img.onerror = function(){ reject('圖片載入失敗'); } img.src=src; } } var src="https://wwww.img.address"; var result = loadimg(src); // 規定:then只接受一個成功的引數,最後統一用catch來捕獲錯誤 result.then(function(img){ console.log(1,img.width); return img; }).then(function(img){ console.log(2,img.width); }).catch(function(ex){ // 統一捕獲異常,也會捕獲到reject方法 console.log(ex); })
2. 多個串聯
// 場景:希望載入完第一個,再載入第二個 var src1 = 'img.address'; var result1 = loadImg(src1); var src2 = 'img.address'; var result2 = loadImg(src2); result1.then(function(img1){ console.log('載入第一章圖片',img1.width); return result2// 重要!!! }).then(function(img2){ console.log('載入第二章圖片',img2.width); }).catch(function(ex){ console.log(ex); })
3. promise.all和promise.race
// Promise.all接收一個promise物件的陣列 // 待全部完成之後,統一執行success Promise.all([result1,result2]).then(datas=>{ // 接收到的datas是一個數組,依次包含了多個promise返回的內容 console.log(datas[0]); console.log(datas[1]); }) // Promise.race 接收一個包含多個promise物件的陣列 // 只要有一個完成,就執行success Promise.race([result1,result2]).then(data=>{ // data 是最先執行完成的 promise物件的返回值 console.log(datas); })
4. promise標準
關於“標準”的閒談,標準很重要
任何技術推廣使用都需要一套標準來支撐 如 html js css http等,無規則不成方圓 任何不符合標準的東西,終將會被使用者拋棄
狀態變化
三種狀態:pending fulfilled rejected 初始狀態pending pending 變成fulfilled,或者pending變為rejected 狀態不可逆
then
promise 例項必須實現then這個方法 then()必須可以接收兩個函式作引數 then()返回的必須是一個promise例項
問題解答總結:
1. 基本語法複習 2. 如何異常捕獲(error和reject都要考慮) 3. 多個串聯-鏈式執行的好處 4. promise.all和promise.race 5. promise標準-狀態變化,then函式
五. 介紹一下 async/await
是es7體驗中的。
then只是將callback拆分了
async/await是最直接的同步寫法
const load = async function(){ const result1 = await loadImg(src1); console.log(result1); const result2 = await loadImg(src1); console.log(result2); } load()
語法(和promise的區別和聯絡)
1. 使用await,函式必須用async標識 2. await 後面跟的是一個promise例項 使用了promise,並沒有和promise衝突 完全是同步的寫法,再也沒有回撥函式 但是:改變不了JS單執行緒、非同步的本質 3. 需要安裝babel-polyfill並,引入
六. 總結當前js非同步解決方案
1. jQuery Deferred 2. Promise 3. async/await 4. generator(並不是解決非同步的,原理比較複雜,忽略)