前言
沒事多看看網路面試題 , 多看看沒事
實作連結(可以點開F12看功能)
在網路上看到有面試題 ,題目如下:
1 2 3 4 5 6 7 8 9 10 11 12
| function getElement(cssSelector){ }
(async()=>{ const btn = getElement('button'); while (1){ await btn.waitClick; console.log('按鈕被點擊了!!') } })();
|
看一下需求大概是 調用getElement()
函數,傳入一個 CSS 選擇器 後拿到一個元素,
拿到元素後就可以給這個元素添加事件 來等待他點擊
然後點擊後, 在進入循環 等待下一次點擊
跟普通使用的 onclick
事件很像
差別看起來是用這種方式,就沒有任何回調的東西傳回來
是以一種同步的方式來書寫異步的代碼!!!
實現思路
1 2 3 4 5
| function getElement(cssSelector){ const dom = document.querySelector(cssSelector); return dom; }
|
這時候會發現 他一直循環 -..-+
看起來還有點問題
發現是 await btn.waitClick
裡面的 btn.waitClick 是undefined
他找不到東西,就一直循環!!!
(其實就等於 promise(resolve)
)
所以要在 function 內加上 btn.waitClick 屬性
1 2 3 4 5 6
| function getElement(cssSelector){ const dom = document.querySelector(cssSelector); btn.waitClick = new Promise(); return dom; }
|
這樣就完成可以實現等待點擊的效果。
但是這種寫法有一種缺點是 不能確定每一次都是 click 事件
搞不好有 mouseover
、change
之類的事件
不一定都是click
所以可以改成
1 2 3 4 5 6 7 8 9
| function getElement(cssSelector){ const dom = document.querySelector(cssSelector); btn.waitClick = new Promise(); btn.waitMouseover =new Promise();
return dom; }
|
但這樣寫法就要每個事件都寫一次 !!!太累了
換個想法
如果不這樣寫,改成換來看await
後面是以wait
開頭,就來看看後面的詞是就是我要做的屬性
1 2 3 4 5 6 7 8 9 10 11
| function getElement(cssSelector){ const dom = document.querySelector(cssSelector); const domProxy = new Proxy(dom,{ get(target,key){ console.log(key); } })
return domProxy; }
|
這樣就可以看到 btn 後面的文字是什麼了
接下來看一下是不是已 wait 開頭的文字選項
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| function getElement(cssSelector){ const dom = document.querySelector(cssSelector); const domProxy = new Proxy(dom,{ get(target,key){ if (!key.startsWith('wait')){ return target[key]; }; const even = key.replace('wait','').toLowerCase(); console.log(even); } })
return domProxy; }
|
這樣寫法 就可以顯示其他已 wait 開頭的功能
然後要寫 click 的事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| function getElement(cssSelector){ const dom = document.querySelector(cssSelector); const domProxy = new Proxy(dom,{ get(target,key){ if (!key.startsWith('wait')){ return target[key]; }; const even = key.replace('wait','').toLowerCase(); return new Promise((resolve)=>{ target.addEventListener(even,resolve,{once:true}) }) } })
return domProxy; }
|
上面代表 監聽這個事件,然後調用resolve ,只發生一次
因為代碼是從上到下,不可能反覆監聽,所以要加上只做一次!!
如果需要多次監聽就只要在使用這個Function時,寫上循環即可。
1 2 3 4 5 6 7 8
| (async()=>{ const btn = getElement('button'); while (1){ await btn.waitClick; console.log('按鈕被點擊了!!') } })();
|
完整的代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| function getElement(cssSelector){ const dom = document.querySelector(cssSelector); const domProxy = new Proxy(dom,{ get(target,key){ if (!key.startsWith('wait')){ return target[key]; }; const even = key.replace('wait','').toLowerCase(); return new Promise((resolve)=>{ target.addEventListener(even,resolve,{once:true}) }) } })
return domProxy; }
(async()=>{ const btn = getElement('button'); while (1){ await btn.waitClick; console.log('按鈕被點擊了!!') } })();
|
如果想要限制點擊次數 例如:10次
只要把循環的方式改成 for 循環 10次 如下:
1 2 3 4 5 6 7
| (async()=>{ const btn = getElement('button'); for (let i = 0 ; i<10 ;i ++){ await btn.waitClick; console.log('按鈕被點擊了!!') } })();
|
這樣十次後就不監聽了!
如果想要拿到事件的參數 在後面添加個 e 就可以了
1 2 3 4 5 6 7
| (async()=>{ const btn = getElement('button'); for (let i = 0 ; i<10 ;i ++){ const e =await btn.waitClick; console.log('按鈕被點擊了!!', e) } })();
|