閉包
閉包指的是那些引用了另一個(gè)函數(shù)作用域中變量的函數(shù)狭吼,通常是在嵌套函數(shù)中實(shí)現(xiàn)的。
概述:閉包是一種書寫代碼一種結(jié)構(gòu)肌括,這種結(jié)構(gòu)擁有的特性就是內(nèi)部的空間在使用中不會(huì)被回收怜俐。(內(nèi)部的變量以及對(duì)應(yīng)的參數(shù)不會(huì)被gc回收)
函數(shù)的預(yù)編譯過程
- 開辟函數(shù)的內(nèi)存空間
- 開辟一個(gè)代碼的對(duì)應(yīng)的內(nèi)存空間。
- 將對(duì)應(yīng)的代碼塊的引用放入這個(gè)函數(shù)的內(nèi)存空間
函數(shù)的執(zhí)行過程
- 開辟一個(gè)執(zhí)行空間(執(zhí)行棧)
- 將對(duì)應(yīng)的函數(shù)的里面的代碼塊內(nèi)容的放到方法執(zhí)行棧中進(jìn)行執(zhí)行
- 執(zhí)行完 對(duì)應(yīng)的執(zhí)行空間銷毀了 那么對(duì)應(yīng)的里面的代碼塊內(nèi)容也就銷毀(gc 回收)
函數(shù)的示例
function reduce(){
var i = 0
i++
}
reduce() //1
reduce() //1
//因?yàn)槔锩娴膇執(zhí)行完以后就會(huì)銷毀 那么對(duì)應(yīng)每次創(chuàng)建的i這個(gè)變量都是一個(gè)新的變量
- 通過上述的示例代碼睹酌,我們知道對(duì)應(yīng)的每次執(zhí)行函數(shù)权谁,里面的變量或者參數(shù)都會(huì)重新聲明,這個(gè)也就意味著每次里面的變量或參數(shù)都是一個(gè)新的變量憋沿,這個(gè)時(shí)候有些情況我們就不滿足旺芽。那么如果我想讓他里面的變量不回收,是不是需要有個(gè)內(nèi)容給他保持引用辐啄,我的變量就不會(huì)被gc 回收采章。
- 根據(jù)上述講解 我們就可以得到 為什么i為全局變量的時(shí)候 倆次的值不一樣
var i = 0
function reduce(){
i++
}
reduce() //1
reduce() //2
// 因?yàn)閕保持對(duì)應(yīng)的引用 所以沒有被回收
- 那么根據(jù)上述可得 如果我在函數(shù)內(nèi)返回對(duì)應(yīng)的一個(gè)引用,在這個(gè)引用內(nèi)包含對(duì)i的引用 那么i 是不是也不會(huì)被回收壶辜。
function reduce(){
var i = 0
i++
retrun {
i
}
}
let obj = reduce()
console.log(obj.i)//1
obj.i++
console.log(obj.i)//2
- 根據(jù)上述代碼 就是如果我的函數(shù)里面返回一個(gè)引用 這個(gè)引用內(nèi)包含了對(duì)應(yīng)外部函數(shù)的參數(shù)或變量悯舟,那么我的這個(gè)外部函數(shù)的參數(shù)他不會(huì)隨著函數(shù)里面代碼的塊的銷毀而銷毀。那么我同時(shí)又知道函數(shù)也是引用數(shù)據(jù)類型砸民,那么我們是不是可以直接返回函數(shù)(方便調(diào)用)抵怎。內(nèi)部函數(shù)包含外部函數(shù)的變量 這個(gè)時(shí)候?qū)?yīng)的變量也不會(huì)被銷毀了奋救。
function reduce(){
var i = 0
return function(){
i++
return i
}
}
//調(diào)用
let fn = reduce()
console.log(fn()) //1
console.log(fn()) //2
函數(shù)嵌套函數(shù)(返回函數(shù)),內(nèi)部函數(shù)保持對(duì)應(yīng)外部函數(shù)的變量(參數(shù))的引用反惕。 這個(gè)稱為閉包(外部的變量不會(huì)被gc回收)尝艘。
閉包的特性
- 可以通過內(nèi)部函數(shù)引用 ,在調(diào)用內(nèi)部函數(shù)的過程中訪問外部函數(shù)的變量姿染。
- 外部函數(shù)的參數(shù)不會(huì)被gc 回收
- 內(nèi)部函數(shù)時(shí)刻保持對(duì)應(yīng)外部函數(shù)的引用
閉包的優(yōu)點(diǎn)
- 不會(huì)被gc回收
- 擴(kuò)大外部函數(shù)內(nèi)變量的使用范圍
- 避免函數(shù)內(nèi)的變量被外部所污染
閉包的缺點(diǎn)
- 導(dǎo)致內(nèi)存泄漏(內(nèi)存溢出) 消耗內(nèi)存
- 內(nèi)部函數(shù)要時(shí)刻保持對(duì)應(yīng)外部函數(shù)的引用
閉包的運(yùn)用
- 作為緩存
- 防抖 節(jié)流
- 函數(shù)柯里化
防抖
概述:在規(guī)定時(shí)間內(nèi)只執(zhí)行一次(執(zhí)行最后一次)
電梯關(guān)門案例
- a 進(jìn)入電梯 等待5s后 就可以上升了
- 在a等待了4s中后 b過來 那么之前的等待就結(jié)束了 開始新的等待
- 在b等待了3s后 c過來 那么之前的等待也結(jié)束了 開始新的等待
- .... 直到最后一次等待結(jié)束 電梯就上升 (實(shí)際電梯上升這個(gè)操作 只執(zhí)行一次 是最后一次)
防抖的實(shí)現(xiàn)
//執(zhí)行的函數(shù) 等待的時(shí)間
function debounce(fn,delay){
var timer = null //記錄有沒有人在等
return function(){
if(timer) clearTimeout(timer) //有人等 上次等待清除
timer = setTimeout(fn,delay) //開始新的等待
}
}
示例
let fn = debounce(function(){
console.log('移進(jìn)去了');
},500)
//div移進(jìn)只執(zhí)行最后一次
document.querySelector('div').onmouseenter = function(){
fn()
}
節(jié)流
概述: 在一定時(shí)間范圍內(nèi) 執(zhí)行第一次 (減少執(zhí)行次數(shù))
高鐵上廁所案例
- 當(dāng)燈是綠燈的時(shí)候 a進(jìn)去了 這個(gè)燈變紅燈
- a 沒有執(zhí)行完的時(shí)候 b進(jìn)不去 只有當(dāng)a 執(zhí)行完 把紅燈變成綠燈 b才能進(jìn)去
- 同樣的 c也是一樣的
function throttle(fn,delay){
var timer = null //默認(rèn)沒有人
return function(){
//判斷當(dāng)前是否有人
if(timer) return //有人不做了 判斷節(jié)流閥是否開啟
timer = setTimerout(()=>{
fn()
timer = null //做完了 將節(jié)流閥關(guān)閉
},delay)
}
}
示例
let fn = throttle(function(){
console.log('移進(jìn)去了');
},5000)
document.querySelector('div').onmouseenter = function(){
fn()
}
函數(shù)柯里化(顆帘澈ィ化 )
概述:拆,將一個(gè)多個(gè)參數(shù)的函數(shù)悬赏,拆分為多個(gè)一個(gè)參數(shù)的函數(shù)狡汉,自由的組合,調(diào)用后續(xù)加強(qiáng)更好的更改闽颇。
參數(shù)傳滿了 返回的是對(duì)應(yīng)的值 參數(shù)沒傳滿返回的是函數(shù)
本身的函數(shù)
function sum(a,b){
return a + b
}
sum(1,2) //3
簡單函數(shù)柯里化
//函數(shù)柯里化
function fn(a){
return function(b){
return a + b
}
}
fn(1)(2) //3
高階函數(shù)柯里化封裝(底層的支持 相關(guān)的維護(hù))
//如果我的參數(shù)有100個(gè) 需要一個(gè)加強(qiáng)的函數(shù) 柯里化封裝的函數(shù)
//fn 表示普通函數(shù)
function curring(fn){
//獲取第一個(gè)傳入的所有參數(shù) 截取 arguments
let arg = Array.prototype.slice.call(arguments,1) //Array.from(arguments).slice(1)
return function(){
let newArg = Array.from(arguments).concat(arg) //將前面的參數(shù)和后面的參數(shù)連接
//參數(shù)沒到繼續(xù)返回函數(shù) 參數(shù)到了才返回值
if(newArg.length<fn.length){
//繼續(xù)調(diào)用curring方法 第一個(gè)參數(shù)傳遞 函數(shù) 第二個(gè)傳遞所有的參數(shù)
return curring.call(this,fn,...newArg)
}else{
return fn(...newArg)
}
}
}
let sum2 = curring(sum1,1)
// console.log(curring(sum1,1));
console.log(sum2(2)(3));
let sum3 = curring(sum1)
console.log(sum3(2)()()()()(3)()()(1));
promise
概述:promise是es6新增的用于解決回調(diào)地獄問題的一個(gè)類盾戴。
回調(diào)地獄
回調(diào)函數(shù)解決了異步的問題
setTimeout(()=>{
console.log('hello')
},0)
setTimeout(()=>{
console.log('hi')
},0)
console.log('吃飯')
//吃飯 === hello === hi
//正常順序 hello === hi === 吃飯(使用回調(diào)函數(shù))
setTimeout(()=>{
console.log('hello')
setTimeout(()=>{
console.log('hi')
console.log('吃飯')
},0)
},0)
概述:回調(diào)函數(shù)的無限嵌套 這個(gè)被稱為回調(diào)地獄,它并不報(bào)錯(cuò),只是這個(gè)代碼失去了價(jià)值(沒有了可維護(hù)性和可閱讀性)
//聲明一個(gè)函數(shù)fn 里面?zhèn)魅肓艘粋€(gè)f的回調(diào)函數(shù)
function fn(f) {
console.log('hello')
f() //執(zhí)行回調(diào)函數(shù)
}
//可維護(hù)性沒有了 可讀性也沒有了 當(dāng)一個(gè)代碼的可維護(hù)性和可讀性都沒有了那么這個(gè)代碼就沒書寫的必要
//這個(gè)被稱為回調(diào)地獄 這個(gè)代碼并不會(huì)報(bào)錯(cuò) 但是他失去了可維護(hù)性和可讀性 就沒有了價(jià)值
fn(function () {
console.log('你好')
fn(function () {
console.log('世界')
fn(function () {
console.log('吃飯')
fn(function () {
console.log('睡覺')
fn(function () {
console.log('打豆豆')
//....
})
})
})
})
})
- promise 可以解決異步的執(zhí)行問題 被設(shè)計(jì)為同步的 內(nèi)部是異步的(異步代碼)
- promise是es6新的增的類 它可以通過通過new關(guān)鍵詞來構(gòu)建(里面?zhèn)鬟f是一個(gè)函數(shù) 這個(gè)函數(shù)里面包含了倆個(gè)形參)
new Promise((resolve,reject)=>{
//代碼
setTimeout(()=>{
console.log('hello')
})
})
promise簡介
promise 翻譯為中文叫做承諾进萄,它具備三種狀態(tài) 等待狀態(tài) 成功狀態(tài) 失敗狀態(tài)
成功狀態(tài)(做相關(guān)處理)then 表示成功以后調(diào)用的函數(shù)(resolve函數(shù)調(diào)用 表示成功)
失敗狀態(tài) (做相關(guān)處理)catch 表示失敗以后調(diào)用的函數(shù) (reject函數(shù)調(diào)用 表示失斈聿薄)
等待狀態(tài) (沒有處理)
//promise三種狀態(tài) 等待 成功 失敗
//每次只有一種狀態(tài) 一旦成功就不會(huì)失敗
new Promise((resolve,reject)=>{
//resolve表示成功 它是一個(gè)函數(shù) 它的調(diào)用表示成功
// resolve('成功了') //resolve里面?zhèn)鬟f的參數(shù)會(huì)到then里面
//reject表示失敗 它也是一個(gè)函數(shù) 它的調(diào)用表示失敗
reject('失敗了') //傳遞值到catch里面
})
// .then(data=>{
// console.log(data) //resolve傳遞的參數(shù)
// }) //表示成功 里面也是個(gè)函數(shù)
.catch(error=>{
console.log(error);
}) //里面也是一個(gè)函數(shù) 里面的值就是reject里面?zhèn)鬟f的值
- resolve里面?zhèn)鬟f的參數(shù)會(huì)到then里面
- reject里面?zhèn)鬟f的參數(shù)會(huì)到catch里面
注意:無論 resolve()和 reject()中的哪個(gè)被調(diào)用锐峭,狀態(tài)轉(zhuǎn)換都不可撤銷了中鼠。于是繼續(xù)修改狀態(tài)會(huì)靜默
失敗。
let p = new Promise((resolve, reject) => {
resolve();
reject(); // 沒有效果
});
setTimeout(console.log, 0, p); // Promise <resolved>
promise的方法
靜態(tài)方法
all 傳入一個(gè)promise數(shù)組(可迭代對(duì)象) 并行執(zhí)行 返回一個(gè)promise實(shí)例(所有的成功才會(huì)返回成功的 有一個(gè)失敗直接返回的失斞伛)
let promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第一個(gè)promise');
resolve('1')
}, 10)
})
let promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第2個(gè)promise');
reject('2')
}, 20)
})
let promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第3個(gè)promise');
resolve('3')
}, 30)
})
let promise4 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第4個(gè)promise');
reject('4')
}, 40)
})
//執(zhí)行里面所有的promise 傳入promise數(shù)組(實(shí)現(xiàn)了迭代器) 返回一個(gè)promise實(shí)例 (reject調(diào)用報(bào)錯(cuò))
//then調(diào)用 要等所有成功了的才能返回 .catch 只要有一個(gè)reject調(diào)用它就會(huì)觸發(fā)
let result = Promise.all([promise1,promise2,promise3,promise4])
result.catch(err=>{
console.log(err);
})
race 競速 傳入一個(gè)promise數(shù)組(可迭代對(duì)象) 并行執(zhí)行里面promise 返回第一個(gè)執(zhí)行的完的promise實(shí)例(鏡像)
//競速 并行執(zhí)行里面promise 返回最先執(zhí)行完的promise(不管是成功還是失斣汀)
let first = Promise.race([promise1,promise2,promise3,promise4])
first.then(data=>{
console.log(`data`, data);
})
reject 傳遞一個(gè)數(shù)據(jù) 返回一個(gè)失敗的promise對(duì)象
//reject 表示失敗 resolve 成功
// 返回promise對(duì)象
let rejectPromise = Promise.reject('hello')
rejectPromise.catch(err=>{
console.log(err);
})
resolve 傳遞一個(gè)數(shù)據(jù) 返回一個(gè)成功的promise對(duì)象
let resolvePromise = Promise.resolve('hi')
resolvePromise.then(data=>{
console.log(data);
})
allSettled 傳遞一個(gè)promise數(shù)組 不相互依賴執(zhí)行 返回一個(gè)promise實(shí)例 里面包含了所有執(zhí)行的promise結(jié)果(有一個(gè)成功就成功了)
const pro1 = Promise.resolve(3);
const pro2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const pro = [pro1, pro2];
//不相互依賴執(zhí)行 返回所有promise的結(jié)果
Promise.allSettled(pro).
then((results) => results.forEach((result) => console.log(result.status)));
非靜態(tài)方法
then
catch
finally
// then 成功的處理
// catch 失敗的處理
// 不管成功還是失敗都會(huì)調(diào)用的 finally
promise1.finally(()=>{
console.log('完成了');
})
promise解決回調(diào)地獄
通過在then方法里面返回一個(gè)新的promise對(duì)象來解決回調(diào)地獄的問題
//promise來解決回調(diào)地獄
new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第一句');
resolve()
}, 20)
}).then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第二句');
resolve()
}, 10)
})
}).then(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第三句');
resolve()
}, 5)
})
}).then(()=>{
console.log('第四句');
})
es7 async await
概述:
es7 新增async關(guān)鍵詞 和 await關(guān)鍵詞 ,async是用于修飾(聲明)函數(shù)的椎扬,await是用來修飾promise的惫搏,async修飾的函數(shù)會(huì)返回一個(gè)新的promise對(duì)象,await只能在async修飾的函數(shù)內(nèi)使用蚕涤。await會(huì)使當(dāng)前的線程陷入阻塞筐赔,只有當(dāng)前await修飾的promise完成后面的才能執(zhí)行( 當(dāng)前線程才會(huì)放開 )。
async
- 修飾函數(shù)的會(huì)返回的新的promise對(duì)象
- 函數(shù)內(nèi)部return語句返回的值揖铜,會(huì)成為then方法回調(diào)函數(shù)的參數(shù)茴丰。
- 函數(shù)內(nèi)部拋出錯(cuò)誤,會(huì)導(dǎo)致返回的 Promise 對(duì)象變?yōu)閞eject狀態(tài)天吓。拋出的錯(cuò)誤對(duì)象會(huì)被catch方法回調(diào)函數(shù)接收到贿肩。
// async修飾函數(shù) 修飾的函數(shù)它會(huì)返回一個(gè)promise對(duì)象
// 里面的return 相當(dāng) resolve調(diào)用
// 里面的報(bào)錯(cuò) 相當(dāng)于 reject調(diào)用
async function fn() {
setTimeout(() => {
console.log('hello')
},0)
// throw new Error('你錯(cuò)了')
return '吃飯了碼'
}
console.log(fn()); //promise對(duì)象
let promise = fn()
promise.then((value)=>{
console.log('world');
console.log(value);
})
// promise.catch((err)=>{ //來到catch
// console.log('world');
// console.log(err);
// })
await
- await 只能在async里面使用
- 正常情況下,await命令后面是一個(gè) Promise 對(duì)象龄寞,返回該對(duì)象的結(jié)果汰规。如果不是 Promise 對(duì)象,就直接返回對(duì)應(yīng)的值物邑。
- 等待它會(huì)使當(dāng)前的線程進(jìn)行等待(只有當(dāng)前的promise執(zhí)行完(不管成功還是失斄锵)它才會(huì)放開當(dāng)前等待)
async function hello(){
await new Promise((resolve,reject)=>{ //線程等待
console.log(1);
resolve() //完成以后 線程放行
})
await new Promise((resolve,reject)=>{ //線程等待
console.log(2);
resolve() //完成以后 線程放行
})
console.log(3);
}
hello()