JavaScript自定義Promise實(shí)現(xiàn)流程
1. 初始結(jié)構(gòu)
我們先來回顧下 js 里 promise 實(shí)例是如何創(chuàng)建的:
let promsie = new Promise((resolve, reject) => { resolve('完成了') })
那這樣我們就知道了,在我們創(chuàng)建的 promise 類中的構(gòu)造函數(shù)要傳入的是一個(gè)函數(shù),而且我們在傳入?yún)?shù)的時(shí)候,這個(gè)函數(shù)參數(shù)會(huì)被自動(dòng)執(zhí)行,接下來這個(gè)函數(shù)參數(shù)也有自己的參數(shù),也就是 resolve 和 reject 這兩個(gè)參數(shù),那我們也得讓我們手寫的 myPromise 支持這兩個(gè)參數(shù):
class myPromise { constructor(func) { func(resolve,reject) } }
這么寫顯然是有問題的,因?yàn)槲覀儾恢涝谀睦镎{(diào)用 resolve 和 reject 這兩個(gè)參數(shù),因?yàn)槲覀冞€沒有定義這兩個(gè)對象,這兩個(gè)對象還是函數(shù),因此下面就要用類方法的形式創(chuàng)建這兩個(gè)函數(shù):
class myPromise { constructor(func) { func(resolve,reject) } resolve() {} reject() {} }
這樣是對的了么?
錯(cuò)啦,因?yàn)槲覀兊糜?this 才能調(diào)用自身的方法,所以要在兩個(gè)參數(shù)前加上 this:
class myPromise { constructor(func) { func(this.resolve,this.reject) } resolve() {} reject() {} }
2. 定義狀態(tài)
promise 里有三種狀態(tài),分別是 pending,fulfilled 和 rejected,要注意狀態(tài)一經(jīng)改變就不能再改變了且只能由 pending 轉(zhuǎn)化為 fulfilled 或者 pending 轉(zhuǎn)化為 rejected。所以我們可以提前把這些狀態(tài)定義好,可以用 const 來創(chuàng)建外部的固定變量,然后將 pending 設(shè)置為默認(rèn)狀態(tài),這樣在每一個(gè)實(shí)例被創(chuàng)建以后就會(huì)有自身的狀態(tài)屬性可以進(jìn)行判斷和變動(dòng)了:
class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒絕' constructor(func) { this.status = myPromise.PENDING func(this.resolve,this.reject) } resolve() { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED } } reject() { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED } } }
當(dāng)執(zhí)行 reolve 的時(shí)候,如果現(xiàn)在的狀態(tài)是待定,就讓他變?yōu)槌晒Γ?reject 同理。
并且,在執(zhí)行 resolve 或者 reject 的時(shí)候都是可以傳入一個(gè)參數(shù)的,這樣后面就能使用這個(gè)參數(shù)了:
class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒絕' constructor(func) { this.status = myPromise.PENDING this.result = null func(this.resolve,this.reject) } resolve(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } reject(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } }
那現(xiàn)在我們 new 一個(gè) myPromise 的實(shí)例,大家覺得會(huì)不會(huì)成功呢?
3. this指向
承接上文,我們 new 了一個(gè)實(shí)例對象:
let promise = new myPromise((resolve,reject) => { resolve('小杰學(xué)前端') })
下面是輸出結(jié)果:
控制臺(tái)不出意外的報(bào)錯(cuò)了,其實(shí)問題就出在這里:
我們在 resolve 中拿不到 status ,也就是 this 已經(jīng)跟丟了,這是為什么呢?
因?yàn)槲覀冊?new 一個(gè)新實(shí)例的時(shí)候,執(zhí)行的是 constructor 里的內(nèi)容,也就是 constructoe 里的 this 指向的是實(shí)例對象,但是現(xiàn)在我們是在實(shí)例對象被創(chuàng)建后再在外部環(huán)境下執(zhí)行 resolve 方法的,這里的 resolve 看著像是和實(shí)例一起執(zhí)行的,其實(shí)不然。解決 class 的 this 指向問題一般會(huì)用箭頭函數(shù), bind 或者 proxy,這里我們就用 bind 來綁定
class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒絕' constructor(func) { this.status = myPromise.PENDING this.result = null func(this.resolve.bind(this),this.reject.bind(this)) } resolve(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } reject(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } }
這里就是給實(shí)例的 resolve 和 reject 方法綁定這個(gè) this 為當(dāng)前的實(shí)例對象
或者我們也可以這樣解決:
class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒絕' constructor(func) { this.status = myPromise.PENDING this.result = null let resolve = (result) => { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } let reject = (result) => { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } func(resolve, reject) } }
4. then 方法
我們先回顧一下原生的 then 是怎么寫的:
let promise = new Promise((resolve, reject) => { resolve('完成了') reject('失敗了') }) promise.then( res => console.log(res), result => console.log(result))
那我們就開始寫我們的 then ,首先知道的是要傳遞兩個(gè)參數(shù),他們都是函數(shù)類型,一個(gè)表示狀態(tài)成功時(shí)的回調(diào),一個(gè)表示狀態(tài)失敗時(shí)的回調(diào):
class myPromise { static PENDING = '待定' static FULFILLED = '成功' static REJECTED = '拒絕' constructor(func) { this.status = myPromise.PENDING this.result = null func(this.resolve.bind(this),this.reject.bind(this)) } resolve(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result } } reject(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result } } then(onFulfilled, onRejected) { if (this.status === myPromise.FULFILLED) { onFulfilled(this.result) } if (this.status === myPromise.REJECTED) { onRejected(this.result) } } }
這里 then 寫的也是淺顯易懂,我們測試一下能否正常輸出:
let promise = new myPromise((resolve,reject) => { resolve('成功了') reject('失敗了') } promise.then(res => console.log(res), result => console.log(result)) // 成功了
5. 執(zhí)行異常
如果我們在 new Promise 的時(shí)候,執(zhí)行函數(shù)里面我們拋出錯(cuò)誤,是會(huì)觸發(fā)拒絕方法,可以把錯(cuò)誤的信息作為內(nèi)容輸出出來:
let promise = new Promise((resolve, reject) => { throw new Error('拋出錯(cuò)誤') }) promise.then( res => console.log(res), result => console.log(result)) //Error: 拋出錯(cuò)誤
但是如果我們在 myPromise 這里寫上同樣的代碼則會(huì)報(bào)錯(cuò),于是我們就可以在執(zhí)行 resolve 和 reject 之前進(jìn)行判斷,可以用 try 和 catch 來該寫代碼,如果沒有報(bào)錯(cuò)就正常執(zhí)行,有報(bào)錯(cuò)就調(diào)用 reject 方法:
constructor(func) { this.status = myPromise.PENDING this.result = null try { func(this.resolve.bind(this),this.reject.bind(this)) } catch (error) { this.reject(error) } }
注意,在 catch 這里不需要用 bind ,因?yàn)檫@是立即調(diào)用的,只在內(nèi)部執(zhí)行的。
那到了這里我們的異常處理是不是和原生的差不多了呢,其實(shí)還沒有,我們知道在 then 里傳遞的是兩個(gè)函數(shù)參數(shù),那如果我們故意在原生代碼里不傳入函數(shù)作為參數(shù)呢:
let promise = new Promise((resolve, reject) => { resolve('成功了') }) promise.then( undefined, result => console.log(result))
實(shí)際上,這里什么也沒有輸出,但是如果我們以同樣的方法應(yīng)用在 myPromise 里會(huì)發(fā)生什么呢?
答案是會(huì)報(bào)一堆錯(cuò)誤,這不是我們想要的,那我們只需要在執(zhí)行 then 里的參數(shù)前進(jìn)行類型判斷,如果它是函數(shù)就把原函數(shù)參數(shù)賦給它,否則就賦給它空函數(shù):
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} if (this.status === myPromise.FULFILLED) { onFulfilled(this.result) } if (this.status === myPromise.REJECTED) { onRejected(this.result) } }
現(xiàn)在再運(yùn)行就不會(huì)報(bào)錯(cuò)了。
6. 支持異步(重頭戲1)
現(xiàn)在我們的手寫代碼里面還沒有植入異步功能,首先我們先了解一下原生 Promise 的一些運(yùn)行順序規(guī)則:
console.log(111) let promise = new Promise((resolve, reject) => { console.log(222) resolve('成功了') }) console.log(333) promise.then( res => console.log(res), result => console.log(result))
熟悉 JS 執(zhí)行機(jī)制和 prmoise 的同學(xué)一定能夠正確的判斷輸出的順序:
我們再看一下手寫的 myPromise 是如何輸出的:
console.log(111) let promise = new myPromise((resolve,reject) => { console.log(222) resolve('成功了') }) promise.then(res => console.log(res), result => console.log(result)) // 成功了 console.log(333)
執(zhí)行代碼,查看輸出:
那我們已經(jīng)找到問題的所在了,那就是 resolve 和 reject 里面都沒有設(shè)置異步執(zhí)行,二話不說直接套上定時(shí)器:
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} if (this.status === myPromise.FULFILLED) { setTimeout(() => { onFulfilled(this.result) }, 0); } if (this.status === myPromise.REJECTED) { setTimeout(() => { onRejected(this.result) }, 0); } }
現(xiàn)在我們的 then 里面就是異步執(zhí)行的了,再次運(yùn)行代碼查看輸出:
可以看到我們成功輸出了,但是現(xiàn)在異步的問題真的解決了嗎,現(xiàn)在又進(jìn)入另一個(gè)非常!非常!重要的重點(diǎn)了,我們先看一下原生 Promise 這段代碼:
console.log(111) let promise = new Promise((resolve, reject) => { console.log(222) setTimeout(() => { resolve('成功了') console.log(444) }, 0); }) console.log(333) promise.then( res => console.log(res), result => console.log(result))
這段代碼的輸出如下:
在 setTimeout 中會(huì)先執(zhí)行輸出444,再執(zhí)行 resolve ,我們再看一下同樣的代碼放在 myPromise 中的輸出結(jié)果:
并沒有輸出 “ 成功了 ”,這是為什么呢?
沒有輸出很可能的原因是 then 方法沒有被執(zhí)行,我們再各個(gè)位置輸出一下當(dāng)前的狀態(tài),進(jìn)行判斷:
console.log(111) let promise = new myPromise((resolve,reject) => { console.log(222) setTimeout(() => { console.log(myPromise.status) resolve('成功了') console.log(myPromise.status) console.log(444) }, 0); }) promise.then(res => { console.log(res); console.log(myPromise.status) }, result => console.log(result)) // 成功了 console.log(333)
看一下控制臺(tái)輸出:
可以看到在 setTimeout 里面的兩個(gè)狀態(tài)都被成功輸出了只有then 里的狀態(tài)并沒有成功輸出出來,那基本就可以判斷是 then 里的判斷出了問題,我們先分析一下,在執(zhí)行完 111,222,333后就開始處理異步,這里肯定是因?yàn)槲覀兿葓?zhí)行了 then ,然后在 then 里會(huì)判斷當(dāng)前狀態(tài)是成功就執(zhí)行成功的回調(diào),是失敗就執(zhí)行失敗的回調(diào),但是如果當(dāng)前狀態(tài)是 pending 呢?這不就出問題了嗎,在本題中 setTimeout 是宏任務(wù),要晚于微任務(wù) then 執(zhí)行,所以 then 執(zhí)行的時(shí)候還沒有改變狀態(tài),自然就不會(huì)輸出,那我們就在 then 里添加待定狀態(tài)的條件就可以了。
7. 回調(diào)保存(重頭戲2)
因?yàn)樵诤瘮?shù)體中執(zhí)行異步操作時(shí),then 會(huì)先執(zhí)行,所以我們要在 then 里狀態(tài)為 pending ,也就是碰到異步操作的時(shí)候讓 then 里的函數(shù)稍后再執(zhí)行,等到狀態(tài)改變了再去執(zhí)行,那我們就定義兩個(gè)數(shù)組來保存回調(diào)
constructor(func) { this.status = myPromise.PENDING this.result = null this.resolveCallbacks = [] this.rejectCallbacks = [] try { func(this.resolve.bind(this),this.reject.bind(this)) } catch (error) { this.reject(error) } }
然后在 then 里狀態(tài)為 pending 時(shí)將函數(shù)保存:
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} if (this.status === myPromise.PENDING) { this.resolveCallbacks.push(() => { onFulfilled(this.result) }) this.rejectCallbacks.push(() => { onRejected(this.result) }) } if (this.status === myPromise.FULFILLED) { setTimeout(() => { onFulfilled(this.result) }, 0); } if (this.status === myPromise.REJECTED) { setTimeout(() => { onRejected(this.result) }, 0); } }
然后在執(zhí)行 resolve 和 reject 的時(shí)候,看看回調(diào)數(shù)組里有沒有待執(zhí)行的函數(shù),有的話就執(zhí)行:
resolve(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result this.resolveCallbacks.forEach(callback => { callback(result) }) } } reject(result) { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result this.rejectCallbacks.forEach(callback => { callback(result) }) } }
現(xiàn)在我們再運(yùn)行一下原來的測試代碼:
console.log(111) let promise = new myPromise((resolve,reject) => { console.log(222) setTimeout(() => { resolve('成功了') console.log(444) }, 0); }) promise.then(res => { console.log(res) }, result => console.log(result)) console.log(333)
現(xiàn)在輸出的是:
這個(gè)輸出結(jié)果也不是我們預(yù)期的呀,我們雖然成功輸出了執(zhí)行成功的回調(diào),但是應(yīng)該是在444后面輸出才對,其實(shí)這里還有個(gè)小坑,就是 resolve 和 reject 也得讓他倆變成異步執(zhí)行:
resolve(result) { setTimeout(() => { if (this.status === myPromise.PENDING) { this.status = myPromise.FULFILLED this.result = result this.resolveCallbacks.forEach(callback => { callback(result) }) } }, 0); } reject(result) { setTimeout(() => { if (this.status === myPromise.PENDING) { this.status = myPromise.REJECTED this.result = result this.rejectCallbacks.forEach(callback => { callback(result) }) } }, 0); }
執(zhí)行結(jié)果:
因?yàn)槲覀冎涝?promise 的函數(shù)體內(nèi)是同步執(zhí)行的,promise.then 是微任務(wù),會(huì)異步執(zhí)行,所以在函數(shù)體內(nèi)碰到 resolve 時(shí)雖然狀態(tài)改變了但是并不會(huì)立刻輸出 result ,但是因?yàn)楝F(xiàn)在我們的函數(shù)體內(nèi)有異步代碼,此時(shí)我們執(zhí)行 then 只是把他的回調(diào)保存起來了,等到執(zhí)行回調(diào)的時(shí)候還得恢復(fù)原來的邏輯,也就是等函數(shù)體內(nèi)同步代碼輸出完了再執(zhí)行 then 輸出 resolve 里傳遞的值,所以要給 resolve 和 reject 加 setTimeout 讓他變成異步,不這樣的話在執(zhí)行 resolve 狀態(tài)改變了就會(huì)立刻輸出結(jié)果,這顯然不是我們期待的。
8. 重難點(diǎn)解讀
如果你之前還是云里霧里沒有懂的話,我們再次來解讀一下這段代碼:
console.log(111) let promise = new myPromise((resolve,reject) => { console.log(222) setTimeout(() => { resolve('成功了') console.log(444) }, 0); }) promise.then(res => { console.log(res) }, result => console.log(result)) console.log(333)
- 第一步,執(zhí)行 console.log(111),輸出 ”111“
- 第二步,創(chuàng)建 promise,同步執(zhí)行函數(shù)體內(nèi)的代碼,執(zhí)行 console.log(222),輸出 “222”
- 第三步,遇到 setTimeout,這是宏任務(wù),將他放到宏任務(wù)隊(duì)列,等待執(zhí)行
- 第四步,遇到 promise.then ,這是微任務(wù),將它放到微任務(wù)隊(duì)列,等待執(zhí)行
- 第五步,執(zhí)行 console.log(333) ,輸出 “333”
- 第六步,當(dāng)前執(zhí)行棧已經(jīng)清空,先執(zhí)行微任務(wù)隊(duì)列的任務(wù) promise.then ,發(fā)現(xiàn)當(dāng)前狀態(tài)還是待定,所以沒有輸出,沒有輸出的原因是改變狀態(tài)的 resolve 函數(shù)在 setTimeout 里,而我們目前還沒有執(zhí)行,然后將 then 的回調(diào)保存起來。
- 第七步,微任務(wù)隊(duì)列已經(jīng)清空,開始執(zhí)行宏任務(wù) setTimeout,因?yàn)?resolve 也是異步執(zhí)行的,所以放到異步隊(duì)列中,先執(zhí)行同步任務(wù) console.log(444),輸出 “444”
- 第八步,現(xiàn)在沒有要執(zhí)行的同步任務(wù)了,再執(zhí)行 resolve ,改變當(dāng)前狀態(tài)為成功,然后發(fā)現(xiàn)回調(diào)數(shù)組里有待執(zhí)行的函數(shù),執(zhí)行回調(diào)函數(shù),輸出結(jié)果 “成功了”
9. 鏈?zhǔn)焦δ埽ㄖ仡^戲3)
我們先了解一下 then 方法的特點(diǎn),then方法中的成功回調(diào)或者失敗回調(diào),如果返回的是一個(gè)promise ,那么這個(gè) promise 成功或者失敗的值就會(huì)傳遞給下一個(gè) then 中。如果返回的是一個(gè)普通的不是 promise 的值,那就把這個(gè)值傳給下一個(gè) then 。如果執(zhí)行時(shí)出現(xiàn)錯(cuò)誤那就會(huì)執(zhí)行下一個(gè) then 中的失敗的回調(diào)。
注意是參數(shù)函數(shù)的返回值,而不是then
本身的返回值,then
本身肯定是返回promise
的。
那么首先我們現(xiàn)在要做的就是拿到上一個(gè)回調(diào)中返回的值:
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} let newPromise = new myPromise((resolve, reject) => { if (this.status === myPromise.PENDING) { this.resolveCallbacks.push(() => { let res = onFulfilled() }) this.rejectCallbacks.push(() => { let res = onRejected() }) } if (this.status === myPromise.FULFILLED) { setTimeout(() => { let res = onFulfilled(this.result) }, 0); } if (this.status === myPromise.REJECTED) { setTimeout(() => { let res = onRejected(this.result) }, 0); } }) return newPromise }
then
能執(zhí)行的前提是,對應(yīng)的promise
執(zhí)行了resolve
,這樣能拿到resolve
的值。
所以后面的then
能拿到的前提是:前面的then
(返回值是promise
) 將參數(shù)函數(shù)返回值resolve
了。這里面略繞,得好好想想。所以我們現(xiàn)在需要執(zhí)行 resolve 并且把上一個(gè)回調(diào)傳進(jìn)來的值作為參數(shù):
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} let newPromise = new myPromise((resolve, reject) => { if (this.status === myPromise.PENDING) { this.resolveCallbacks.push(() => { let res = onFulfilled(this.result) resolve(res) }) this.rejectCallbacks.push(() => { let res = onRejected(this.result) resolve(res) }) } if (this.status === myPromise.FULFILLED) { setTimeout(() => { let res = onFulfilled(this.result) resolve(res) }, 0); } if (this.status === myPromise.REJECTED) { setTimeout(() => { let res = onRejected(this.result) resolve(res) }, 0); } }) return newPromise }
現(xiàn)在我們的工作完成了么?還沒有,準(zhǔn)確的說核心邏輯完成了一半,我們還沒有處理返回值是 promise 對象的情況,下面我們直接把這塊的邏輯抽象成一個(gè)函數(shù):
then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : () => {} onRejected = typeof onRejected === 'function' ? onRejected : () => {} let newPromise = new myPromise((resolve, reject) => { if (this.status === myPromise.PENDING) { this.resolveCallbacks.push(() => { let res = onFulfilled(this.result) this.resolvePromise(res,resolve,reject) }) this.rejectCallbacks.push(() => { let res = onRejected(this.result) this.resolvePromise(res,resolve,reject) }) } if (this.status === myPromise.FULFILLED) { setTimeout(() => { let res = onFulfilled(this.result) this.resolvePromise(res,resolve,reject) }, 0); } if (this.status === myPromise.REJECTED) { setTimeout(() => { let res = onRejected(this.result) this.resolvePromise(res,resolve,reject) }, 0); } }) return newPromise } resolvePromise(res, resolve, reject) { if(res instanceof myPromise) { res.then(resolve, reject) } else{ // 普通值 resolve(res) } }
那么現(xiàn)在我們通過實(shí)例看一下能否實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用,當(dāng)返回值是 promise 的時(shí)候:
let promise = new myPromise((resolve,reject) => { resolve(11) }) const a = new Promise((resolve,reject) => { resolve('ok') }) promise.then(res => { return a }).then(res => console.log(res))
運(yùn)行結(jié)果:
當(dāng)返回值是普通值的時(shí)候:
let promise = new myPromise((resolve,reject) => { resolve(11) }) promise.then(res => { return res }).then(res => console.log(res)) //11
可以看到現(xiàn)在我們的鏈?zhǔn)秸{(diào)用已經(jīng)基本實(shí)現(xiàn)了功能,其實(shí)這里面還有很多邊界情況沒有做處理,還實(shí)現(xiàn)的很粗糙,但是我們研究源碼要做的就是抽絲剝繭,關(guān)注最核心的邏輯就行,當(dāng)然能把邊界情況都處理了是最好的。
到此這篇關(guān)于JavaScript自定義Promise實(shí)現(xiàn)流程的文章就介紹到這了,更多相關(guān)JavaScript Promise內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
5個(gè)可以幫你理解JavaScript核心閉包和作用域的小例子
這篇文章主要介紹了5個(gè)可以幫你理解JavaScript核心閉包和作用域的小例子,本文是翻譯自國外的一篇文章,短小精悍,需要的朋友可以參考下2014-10-10Javascript的表單驗(yàn)證-揭開正則表達(dá)式的面紗
Javascript的表單驗(yàn)證-揭開正則表達(dá)式的面紗在本文重點(diǎn)介紹,感興趣的朋友一起學(xué)習(xí)吧2016-03-03JSP中使用JavaScript動(dòng)態(tài)插入刪除輸入框?qū)崿F(xiàn)代碼
這篇文章主要介紹了JSP中如何使用JavaScript動(dòng)態(tài)插入刪除輸入框,需要的朋友可以參考下2014-06-06JavaScript中統(tǒng)計(jì)Textarea字?jǐn)?shù)并提示還能輸入的字符
是在文本框中輸入文字的時(shí)候,會(huì)自動(dòng)統(tǒng)計(jì)輸入的字符,并顯示用戶還能輸入的字符,其實(shí)js也可以實(shí)現(xiàn),下面就以示例的方式為大家講解下2014-06-06在TypeScript項(xiàng)目中搭配Axios封裝后端接口調(diào)用
這篇文章主要介紹了在TypeScript項(xiàng)目中搭配Axios封裝后端接口調(diào)用,本文記錄一下在?TypeScript?項(xiàng)目里封裝?axios?的過程,之前在開發(fā)?StarBlog-Admin?的時(shí)候已經(jīng)做了一次封裝,不過那時(shí)是JavaScript跟TypeScript還是有些區(qū)別的,需要的朋友可以參考下2024-01-01JavaScript通過RegExp實(shí)現(xiàn)客戶端驗(yàn)證處理程序
通過RegExp實(shí)現(xiàn)客戶端驗(yàn):讓文本框只允許輸入數(shù)字、文本框只允許輸入中文、郵箱輸入格式的判斷等等,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈2013-05-05js循環(huán)map 獲取所有的key和value的實(shí)現(xiàn)代碼(json)
這篇文章主要介紹了js循環(huán)map 獲取所有的key和value的實(shí)現(xiàn)代碼(json),需要的朋友可以參考下2018-05-05