1.1 區(qū)別實例對象和函數(shù)對象
- 實例對象:
new函數(shù)產(chǎn)生的對象,稱為實例對象,簡稱對象; - 函數(shù)對象:將函數(shù)作為對象使用時,簡稱為函數(shù)對
function Fn() { // Fn函數(shù)
}
const fn = new Fn() // Fn是構造函數(shù) fn是實例對象(簡稱為對象)
console.log(Fn.prototype) // Fn是函數(shù)對象
Fn.call({}) // Fn是函數(shù)對象
// 區(qū)別函數(shù)和函數(shù)對象
// 在于函數(shù)名后加的是括號還是點,括號則是函數(shù),點則是函數(shù)對象
$('#test') // jQuery函數(shù)
$.get('/test') // jQuery函數(shù)對象
function Person(params) {
}
1.2 兩種類型的回調(diào)函數(shù)
回調(diào)函數(shù)必須滿足三個條件:1.我定義的;2.我沒調(diào)用;3.最終調(diào)用了。
1.2.1 同步回調(diào)
- 理解:立即執(zhí)行,完全執(zhí)行完了才結束,不會放入回調(diào)隊列中
- 例子:數(shù)組遍歷相關的回調(diào)函數(shù) /
Promise的excutor函數(shù)(具體看下文)
1.2.2 異步回調(diào)
- 理解:不會立即執(zhí)行,會放入回調(diào)隊列中將來執(zhí)行
- 例子:定時器回調(diào) /
ajax回調(diào) /Promise的成功或失敗的回調(diào)(具體看下文)
如何區(qū)分同步回調(diào)和異步回調(diào)?
在回調(diào)函數(shù)之后打印輸出內(nèi)容;
如果內(nèi)容在回調(diào)函數(shù)執(zhí)行之后輸出,則是同步回調(diào);反之則是異步回調(diào)。
// 1\. 同步回調(diào)函數(shù)
// const arr = [1, 3, 5]
arr.forEach(item => { // 遍歷回調(diào), 同步回調(diào)函數(shù), 不會放入列隊, 一上來就要執(zhí)行完
console.log(item)
})
console.log('forEach()之后')
// 2\. 異步回調(diào)函數(shù)
setTimeout(() => { // 異步回調(diào)函數(shù), 會放入隊列中將來執(zhí)行
console.log('timout callback()')
}, 0)
console.log('setTimeout()之后')
1.3 JS 的 error 處理
1.3.1 錯誤的類型
-
Error:所有錯誤的父類型 -
ReferenceError:引用的變量不存在;如:未聲明之下打印一個變量:console.log(a) -
TypeError:數(shù)據(jù)類型不正確的錯誤,如:把一個普通變量當對象用:let b; console.log(b.xxx) -
RangeError:數(shù)據(jù)值不在其所允許的范圍內(nèi),如:遞歸溢棧 -
SyntaxError:語法錯誤,如:console.log("""")
1.3.2 錯誤處理
- 捕獲處理錯誤:
try...catch - 拋出錯誤:
throw error;由調(diào)用者決定如何處理錯誤,后面可以捕獲再處理
1.3.3 error 對象的結構
error 對象有兩個屬性:message 和 stact
message 屬性:錯誤相關信息
stack 屬性:函數(shù)調(diào)用棧記錄信息(可打斷點查看)
/*
目標: 進一步理解JS中的錯誤(Error)和錯誤處理
mdn文檔: https: //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Error
1\. 錯誤的類型
Error: 所有錯誤的父類型
ReferenceError: 引用的變量不存在
TypeError: 數(shù)據(jù)類型不正確的錯誤
RangeError: 數(shù)據(jù)值不在其所允許的范圍內(nèi)
SyntaxError: 語法錯誤
2\. 錯誤處理
捕獲錯誤: try ... catch
拋出錯誤: throw error
3\. 錯誤對象
message屬性: 錯誤相關信息
stack屬性: 函數(shù)調(diào)用棧記錄信息
*/
// 1\. 常見的內(nèi)置錯誤
// 1). ReferenceError: 引用的變量不存在
// console.log(a) // ReferenceError: a is not defined
// console.log('-----') // 沒有捕獲error, 下面的代碼不會執(zhí)行
// TypeError: 數(shù)據(jù)類型不正確的錯誤
// let b
// // console.log(b.xxx) // TypeError: Cannot read property 'xxx' of undefined
// b = {}
// b.xxx() // TypeError: b.xxx is not a function
// RangeError: 數(shù)據(jù)值不在其所允許的范圍內(nèi)
// function fn() {
// fn()
// }
// fn() // RangeError: Maximum call stack size exceeded
// SyntaxError: 語法錯誤
// const c = """" // SyntaxError: Unexpected string
// 2\. 錯誤處理
// 捕獲錯誤: try ... catch
// try {
// let d
// console.log(d.xxx)
// } catch (error) {
// console.log(error.message)
// console.log(error.stack)
// }
// console.log('出錯之后')
// 拋出錯誤: throw error
function something() {
if (Date.now()%2===1) {
console.log('當前時間為奇數(shù), 可以執(zhí)行任務')
} else { // 如果時間是偶數(shù)拋出異常, 由調(diào)用來處理
throw new Error('當前時間為偶數(shù)無法執(zhí)行任務')
}
}
// 捕獲處理異常
try {
something()
} catch (error) {
alert(error.message)
}
第2章:promise 的理解和使用
2.1 Promise 是什么?
2.1.1 理解
-
抽象表達
Promise是JS中進行異步編程的新的解決方案 -
具體表達
(1)從語法上來說:
Promise是一個構造函數(shù)(2)從功能上來說:
promise對象用來封裝一個異步操作并可以獲取其結果
2.1.2 promise 狀態(tài)改變
promise 對象只能有三種狀態(tài),pending、resolved、rejected,而狀態(tài)改變只有以下兩種情況。
-
pending變?yōu)?resolved -
pending變?yōu)?rejected
說明
狀態(tài)改變只有這兩種,而且一個
promise對象只能改變一次無論變?yōu)槌晒蛘呤?,都會有一個最終結果;成功的結果數(shù)據(jù)一般稱為
value,失敗的結果數(shù)據(jù)一般稱為reason
2.1.3 promise 的基本流程

2.1.4 promise 的基本使用
// 1\. 創(chuàng)建一個新的promise對象
const p = new Promise((resolve, reject) => {// 執(zhí)行器函數(shù) 同步回調(diào)
console.log('執(zhí)行 excutor')
// 2\. 執(zhí)行異步操作任務
setTimeout(() => {
const time = Date.now() // 如果當前時間是偶數(shù)就代表成功, 否則代表失敗
// 3.1\. 如果成功了, 調(diào)用resolve(value)
if (time %2 == 0) {
resolve('成功的數(shù)據(jù), time=' + time)
} else {
// 3.2\. 如果失敗了, 調(diào)用reject(reason)
reject('失敗的數(shù)據(jù), time=' + time)
}
}, 1000);
})
console.log('new Promise()之后')
// setTimeout(() => {
p.then(
value => { // 接收得到成功的value數(shù)據(jù) onResolved
console.log('成功的回調(diào)', value)
},
reason => {// 接收得到失敗的reason數(shù)據(jù) onRejected
console.log('失敗的回調(diào)', reason)
}
// )
// }, 2000);
2.2 為什么要用 promise ?
2.2.1 指定回調(diào)函數(shù)的方式更加靈活
舊的(純回調(diào)函數(shù)):必須在啟動異步任務之前指定
-
promise:啟動異步任務 => 返回promise對象 => 給promise對象綁定回調(diào)函數(shù)(甚至可以在異步任務結束后指定【可加定時器】/多個)(一般情況下異步任務執(zhí)行需要時間嘛,那么就會先執(zhí)行下面的代碼,會調(diào)用
then()方法,從而綁定回調(diào)函數(shù))
2.2.2 支持鏈式調(diào)用,可以解決回調(diào)地獄問題
-
什么是回調(diào)地獄?
回調(diào)函數(shù)嵌套調(diào)用,外部回調(diào)函數(shù)異步執(zhí)行的結果是嵌套的回調(diào)執(zhí)行的條件
(回調(diào)函數(shù)層層嵌套,外面的回調(diào)函數(shù)執(zhí)行完了,才會把結果傳給里面的回調(diào)函數(shù),作為條件)
-
回調(diào)地獄的缺點?
不便于閱讀
不便于異常處理
-
解決方案?
promise鏈式調(diào)用 -
最終解決方案?
async/await
// 成功的回調(diào)函數(shù)
function successCallback(result) {
console.log("聲音文件創(chuàng)建成功: " + result);
}
// 失敗的回調(diào)函數(shù)
function failureCallback(error) {
console.log("聲音文件創(chuàng)建失敗: " + error);
}
/* 1.1 使用純回調(diào)函數(shù) */
createAudioFileAsync(audioSettings, successCallback, failureCallback)
/* 1.2\. 使用Promise */
const promise = createAudioFileAsync(audioSettings); // 2
setTimeout(() => {
promise.then(successCallback, failureCallback);
}, 3000);
/*
2.1\. 回調(diào)地獄
*/
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult)
}, failureCallback)
}, failureCallback)
}, failureCallback)
/*
2.2\. 使用promise的鏈式調(diào)用解決回調(diào)地獄
*/
doSomething()
.then(function(result) {
return doSomethingElse(result)
})
.then(function(newResult) {
return doThirdThing(newResult)
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult)
})
.catch(failureCallback)
/*
2.3\. async/await: 回調(diào)地獄的終極解決方案
*/
async function request() {
try {
const result = await doSomething()
const newResult = await doSomethingElse(result)
const finalResult = await doThirdThing(newResult)
console.log('Got the final result: ' + finalResult)
} catch (error) {
failureCallback(error)
}
}
2.3 如何使用 promise ?
2.3.1 API
-
Promise構造函數(shù):Promise (excutor) {}excutor函數(shù):同步執(zhí)行(resolve, reject) => {}resolve函數(shù):內(nèi)部定義成功時我們調(diào)用的函數(shù)value => {}reject函數(shù):內(nèi)部定義失敗時我們調(diào)用的函數(shù)reason => {} -
Promise.prototype.then方法:(onResolved, onRejected) => {}onResolved函數(shù):成功的回調(diào)函數(shù)(value) => {}onRejected函數(shù):失敗的回調(diào)函數(shù)(reason) => {}說明:指定用于得到成功
value的成功回調(diào)和用于得到失敗reason的失敗回調(diào),返回一個新的promise對象 -
Promise.prototype.catch方法:(onRejected) => {}onRejected函數(shù):失敗的回調(diào)函數(shù)(reason) => {}說明:
then()的語法糖,相當于:then(undefined, onRejected) -
Promise.resolve方法:(value) => {}value:成功的數(shù)據(jù)或promise對象說明:返回一個成功/失敗的
promise對象 -
Promise.reject方法:(reason) => {}reason:失敗的原因說明:返回一個失敗的
promise對象 -
Promise.all方法:(promises) => {}promises:包含n個promise的數(shù)組說明:返回一個新的
promise,只有所有的promise都成功才成功,只要有一個失敗了就直接失敗 -
Promise.race方法:(promises) => {}promise:包含n個promise的數(shù)組說明:返回一個新的
promise,第一個完成的promise的結果狀態(tài)就是最終的結果狀態(tài)
new Promise((resolve, reject) => {
setTimeout(() => {
// resolve('成功的數(shù)據(jù)')
reject('失敗的數(shù)據(jù)')
}, 1000)
}).then(
value => {
console.log('onResolved()1', value)
}
).catch(
reason => {
console.log('onRejected()1', reason)
}
)
// 產(chǎn)生一個成功值為1的promise對象
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 100);
})
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)
// p1.then(value => {console.log(value)})
// p2.then(value => {console.log(value)})
// p3.catch(reason => {console.log(reason)})
// const pAll = Promise.all([p1, p2, p3])
const pAll = Promise.all([p1, p2])
/* pAll.then(
values => {
console.log('all onResolved()', values)
},
reason => {
console.log('all onRejected()', reason)
}
)
*/
const pRace = Promise.race([p1, p2, p3])
pRace.then(
value => {
console.log('race onResolved()', value)
},
reason => {
console.log('race onRejected()', reason)
}
)
2.3.2 promise 的幾個關鍵問題
-
如何改變
promise的狀態(tài)?(1)
resolve(value):如果當前是pending就會變?yōu)?resolved(2)
reject(reason):如果當前是pending就會變?yōu)?rejected(3)拋出異常:如果當前是
pending就會變?yōu)?rejected -
一個
promise指定多個成功/失敗回調(diào)函數(shù),都會調(diào)用嗎?當
promise改變?yōu)閷獱顟B(tài)時都會調(diào)用 -
改變
promise狀態(tài)和指定回調(diào)函數(shù)誰先誰后?(1)都有可能,正常情況下是先指定回調(diào)再改變狀態(tài),但也可以先改變狀態(tài)再指定回調(diào)
(2)如何先改狀態(tài)再指定回調(diào)?
- 在執(zhí)行器中直接調(diào)用
resolve()/reject() - 延遲更長時間才調(diào)用
then()
(3)什么時候才能得到數(shù)據(jù)?
關鍵在于回調(diào)函數(shù)調(diào)用時,才能得到數(shù)據(jù)
如果先指定的回調(diào),那當狀態(tài)發(fā)生改變時,回調(diào)函數(shù)就會調(diào)用,得到數(shù)據(jù)
如果先改變的狀態(tài),那當指定回調(diào)時,回調(diào)函數(shù)就會調(diào)用,得到數(shù)據(jù)
- 在執(zhí)行器中直接調(diào)用
-
promise.then()返回的新promise的結果狀態(tài)由什么決定?(1)簡單表達:由
then()指定的回調(diào)函數(shù)執(zhí)行的結果決定(2)詳細表達:
如果拋出異常,新
promise變?yōu)?rejected,reason為拋出的異常如果返回的是非
promise的任意值,新promise變?yōu)?resolved,value為返回的值如果返回的是另一個新
promise,那么返回的promise的結果就會成為新(原來的)promise結果
-
promise如何串連多個操作任務?(1)
promise的then()返回一個新的promise,可以看成then()的鏈式調(diào)用(2)通過
then的鏈式調(diào)用串連多個同步/異步任務 -
promise異常傳/穿透?(1)當使用
promise的then鏈式調(diào)用時,可以在最后指定失敗的回調(diào)(2)前面任何操作出了異常,都會傳到最后失敗的回調(diào)中處理
-
中斷
promise鏈?(1)當使用
promise的then鏈式調(diào)用時,在中間中斷,不再調(diào)用后面的回調(diào)函數(shù)(2)方法:在回調(diào)函數(shù)中返回一個狀態(tài)為
pending的promise對象