私藏的這些高級(jí)工具函數(shù),你擁有幾個(gè)?

https://juejin.cn/post/6987166546502090788
前言
很多功能,其實(shí)內(nèi)置的Web API已支持,
- 比如基于
URLSearchParams或者URL的queryString獲取和生成 - 比如基于
btoa,atob的base64的編碼和解碼 - 比如基于
sendBeacon的數(shù)據(jù)上報(bào) - 比如基于
Array.from的序列生成 - 比如基于
canvas的視頻截圖 - 比如基于
URL的UUID生成
我們用精簡(jiǎn)的代碼來(lái)實(shí)現(xiàn)相對(duì)復(fù)雜的功能,沒(méi)有第三方庫(kù),你也能秀得飛起。
目錄(方便移動(dòng)端閱讀)
- localStorage的已使用空間
- 帶圖帶事件的桌面通知
- 原生30行代碼實(shí)現(xiàn)視頻截圖
- 基于
URLSearchParams獲取queryString的值 - 基于
atob和btoa的base64編碼和解碼 - 非正則替換html代碼encode和decode
- 相對(duì)地址轉(zhuǎn)換為絕對(duì)地址
- 基于
URL或者Crypto.getRandomValues生成UUID - 基于
Array.from的序列生成器 - 基于
sendBeacon的安全的數(shù)據(jù)上報(bào) - 基于
toLocaleString千分位 - Promise順序執(zhí)行
- 延時(shí)執(zhí)行delay
- 進(jìn)度值映射
- 滑滾動(dòng)頁(yè)面到頂部
- 禁止選擇和復(fù)制
- 禁止圖片拖拽
- 自增長(zhǎng)ID
localStorage的已使用空間
在較新的chrome上測(cè)試,localStorage的存儲(chǔ)是按照字符個(gè)數(shù)來(lái)算的。包含鍵和值的。
所以在測(cè)試代碼中,你把a修改啊,不會(huì)影響存儲(chǔ)的數(shù)量。但是鍵的長(zhǎng)度,會(huì)影響存儲(chǔ)的數(shù)量。
代碼
function?getLSUsedSpace()?{
????return?Object.keys(localStorage).reduce((total,?curKey)?=>?{
????????if?(!localStorage.hasOwnProperty(curKey))?{
????????????return?total;
????????}
????????total?+=?localStorage[curKey].length?+?curKey.length;
????????return?total;
????},?0)
}
復(fù)制代碼
示例
localStorage.clear();
localStorage.a?=?"啊";
console.log(getLSUsedSpace());?//?2
復(fù)制代碼
溢出測(cè)試:
key的值為長(zhǎng)度為10的 kkkkkkkkkk:
輸出結(jié)果:Max: 5242880 value Length: 5242870
當(dāng)你把key修改長(zhǎng)度為1的k:
輸出結(jié)果:Max: 5242880 value Length: 5242879
localStorage.clear();
let?valLength?=?0
try?{
????let?str?=?Array.from({?length:?5242800?},?()?=>?"啊").join("");
????valLength?=?str.length;
????for?(let?i?=?0;?i?<?10000000000000;?i++)?{
????????str?+=?"a"
????????valLength?+=?1;
????????localStorage.setItem(`kkkkkkkkkk`,?str);
????}
}?catch?(err)?{
????console.error("存儲(chǔ)失敗",?err);
????console.log("Max:",?getLSUsedSpace(),?"?value?Length:",?valLength)
}
復(fù)制代碼
image.png注意
- 超過(guò)存儲(chǔ)上線是會(huì)報(bào)錯(cuò)的:
image.png- 如何捕獲錯(cuò)誤,可以參考 MDN testing\_for\_availability[6]
大致是對(duì)Error的錯(cuò)誤碼和name進(jìn)行判斷
e?instanceof?DOMException?&&?(
????????????//?everything?except?Firefox
????????????e.code?===?22?||
????????????//?Firefox
????????????e.code?===?1014?||
????????????//?test?name?field?too,?because?code?might?not?be?present
????????????//?everything?except?Firefox
????????????e.name?===?'QuotaExceededError'?||
????????????//?Firefox
????????????e.name?===?'NS_ERROR_DOM_QUOTA_REACHED')?&&
????????????//?acknowledge?QuotaExceededError?only?if?there's?something?already?stored
????????????(storage?&&?storage.length?!==?0);
復(fù)制代碼
參考引用
calculating-usage-of-localstorage-space[7]
what-is-the-max-size-of-localstorage-values[8]
Test of localStorage limits/quota[9]
帶圖帶事件的桌面通知
網(wǎng)頁(yè)也可以以桌面彈框的形式進(jìn)行通知,先看個(gè)效果圖:
有頭像,有標(biāo)題,有文本,點(diǎn)擊消息通知還能讓窗體聚焦,真帥。
image.png代碼
function?doNotify(title,?options?=?{},?events?=?{})?{
????const?notification?=?new?Notification(title,?options);
????for?(let?event?in?events)?{
????????notification[event]?=?events[event];
????}
}
function?notify(title,?options?=?{},?events?=?{})?{
????if?(!("Notification"?in?window))?{
????????return?console.error("This?browser?does?not?support?desktop?notification");
????}
????else?if?(Notification.permission?===?"granted")?{
????????doNotify(title,?options,?events);
????}?else?if?(Notification.permission?!==?"denied")?{
????????Notification.requestPermission().then(function?(permission)?{???????????
????????????if?(permission?===?"granted")?{
????????????????doNotify(title,?options,?events);
????????????}
????????});
????}
}
復(fù)制代碼
示例
tag還可以用去重消息。
?????notify("中獎(jiǎng)提示",?{
????????????icon:?"https://sf1-ttcdn-tos.pstatp.com/img/user-avatar/f1a9f122e925aeef5e4534ff7f706729~300x300.image",
????????????body:?"恭喜你,掘金簽到一等獎(jiǎng)",
????????????tag:?"prize"
????????},?{
????????????onclick(ev)?{
????????????????console.log(ev);
????????????????ev.target.close();
????????????????window.focus();
????????????}
????????})
復(fù)制代碼
參考引用
notification[10]
使用 Web Notifications[11]
原生30行代碼實(shí)現(xiàn)視頻截圖
基本原理就是把視頻畫(huà)到Canvas里面,然后調(diào)用toDataURL或者toBlob,再利用a標(biāo)簽?zāi)M點(diǎn)擊,download屬性指定名字。
看一下效果:
代碼
?????function?captureVideo(videoEl)?{
????????????let?canvasEl;
????????????let?dataUrl;
????????????try?{
????????????????const?cps?=?window.getComputedStyle(videoEl);
????????????????const?width?=?+cps.getPropertyValue("width").replace("px",?"");
????????????????const?height?=?+cps.getPropertyValue("height").replace("px",?"");
????????????????canvasEl?=?document.createElement("canvas");
????????????????canvasEl.style.cssText?=?`position:fixed;left:-9999px`;
????????????????canvasEl.height?=?height;
????????????????canvasEl.width?=?width;
????????????????document.body.appendChild(canvasEl);
????????????????
????????????????const?ctx?=?canvasEl.getContext("2d");
????????????????ctx.drawImage(videoEl,?0,?0,?width,?height);
????????????????//?const?image?=?canvas.toDataURL("image/png");
????????????????dataUrl?=?canvasEl.toDataURL();
????????????????document.body.removeChild(canvasEl);
????????????????canvasEl?=?null;
????????????????return?dataUrl;
????????????}?finally?{
????????????????if?(canvasEl)?{
????????????????????document.body.removeChild(canvasEl);
????????????????}
????????????????if?(dataUrl)?{
????????????????????return?dataUrl;
????????????????}
????????????}
????????}
復(fù)制代碼
示例
注意添加crossorigin="anonymous",不然轉(zhuǎn)為圖片會(huì)失敗。
??<video?id="videoEL"?controls?autoplay?crossorigin="anonymous"
????????src="https://api.dogecloud.com/player/get.mp4?vcode=5ac682e6f8231991&userId=17&ext=.mp4"?width="500"></video>
function?download(url)?{
????const?aEl?=?document.createElement("a");
????aEl.href?=?url;
????aEl.download?=?"視頻.png";
????aEl.click();
}
function?doCaptureVideo()?{
????const?url?=?captureVideo(videoEL);
????download(url);
}
doCaptureVideo()
復(fù)制代碼
基于URLSearchParams或URL獲取queryString的值
常用的方式是使用正則或者split方法,其實(shí)不然,URLSearchParams和URL都能很好的實(shí)現(xiàn)功能。
代碼
const?urlSP?=?new?URLSearchParams(location.search);
function?getQueryString(key){
????return?urlSP.get(key)
}
const?urlObj?=?new?URL(location.href);
function?getQueryString(key){
????return?urlObj.searchParams.get(key)
}
復(fù)制代碼
示例
測(cè)試地址:?/index.html?pid=10
const?log?=?console.log;
getQueryString
log("pid",?getQueryString("pid"));??//?pid?10
log("cid",?getQueryString("cid"));??//?cid?null
復(fù)制代碼
參考引用
MDN文獻(xiàn):URLSearchParams-MDN[12]
CanIUse兼容性: URLSearchParams: 95.63\%[13]
Polyfill: url-search-params-polyfill[14]
基于atob和btoa的base64編碼和解碼
瀏覽器內(nèi)置了base64編碼和解碼的能力,第三方庫(kù),不需要的。
代碼
function?utf8_to_b64(?str?)?{
??return?window.btoa(unescape(encodeURIComponent(?str?)));
}
function?b64_to_utf8(?str?)?{
??return?decodeURIComponent(escape(window.atob(?str?)));
}
復(fù)制代碼
示例
utf8_to_b64('??à?la?mode');?//?"4pyTIMOgIGxhIG1vZGU="
b64_to_utf8('4pyTIMOgIGxhIG1vZGU=');?//?"??à?la?mode"
復(fù)制代碼
參考引用
MDN文獻(xiàn):atob[15], btoa[16]
CanIUse兼容性: btoa 99.68\%[17]
Polyfill: MDN Polyfill[18]
Base64[19]
非正則替換的html代碼encode和decode
常規(guī)的方式是使用正則替換,這里是另外一種思路。
代碼
function?htmlencode(s){
????var?div?=?document.createElement('div');
????div.appendChild(document.createTextNode(s));
????var?result?=?div.innerHTML;
????div?=?null;
????return?result;
}
function?htmldecode(s){
????var?div?=?document.createElement('div');
????div.innerHTML?=?s;
????var?result?=?div.innerText?||?div.textContent;
????div?=?null;
????return?result;
}
復(fù)制代碼
示例
htmlencode("<div>3>5?&?666</div>");?//?<div>3>5?&?666</div>
htmldecode("<div>3>5?&?666</div>")?//?<div>3>5?&?666</div>
復(fù)制代碼
相對(duì)地址轉(zhuǎn)換為絕對(duì)地址
基于當(dāng)前頁(yè)面的相對(duì)地址轉(zhuǎn)換為絕對(duì)地址。
代碼
function?realativeToAbs(href)?{
????let?aEl?=?document.createElement("a");
????aEl.href?=?href;????
????const?result?=?aEl.href;
????aEl?=?null;
????return?result;
}
復(fù)制代碼
示例
console.log("realativeToAbs",?realativeToAbs("../a/b/b/index.html"));
//?realativeToAbs?http://127.0.0.1:5500/a/b/b/index.html
復(fù)制代碼
基于URL或者Crypto.getRandomValues生成UUID
基于URL.createObjectURL[20]或者Crypto.getRandomValues[21]
URL.createObjectURL 產(chǎn)生的地址為 blob:https://developer.mozilla.org/cb48b940-c625-400a-a393-176c3635020b, 其后部分就是一個(gè)UUID
代碼
方式一:
function?genUUID()?{
????const?url?=?URL.createObjectURL(new?Blob([]));
????//?const?uuid?=?url.split("/").pop();
????const?uuid?=?url.substring(url.lastIndexOf('/')+?1);
????URL.revokeObjectURL(url);
????return?uuid;
}
genUUID()?//?cd205467-0120-47b0-9444-894736d873c7
復(fù)制代碼
方式二:
function?uuidv4()?{
??return?([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,?c?=>
????(c?^?crypto.getRandomValues(new?Uint8Array(1))[0]?&?15?>>?c?/?4).toString(16)
}
uuidv4()?//?38aa1602-ba78-4368-9235-d8703cdb6037
復(fù)制代碼
參考引用
generating-uuids-at-scale-on-the-web-2877f529d2a2[22]
collisions-when-generating-uuids-in-javascript[23]
基于Array.from的序列生成器
造有序數(shù)據(jù),無(wú)序數(shù)據(jù),等等。
代碼
const?range?=?(start,?stop,?step)?=>?Array.from(
????{?length:?(stop?-?start)?/?step?+?1},?
????(_,?i)?=>?start?+?(i?*?step)
);
復(fù)制代碼
示例
range(0,?4,?1);?//?[0,?1,?2,?3,?4]
range(0,?9,?3);?//?[0,?3,?6,?9]
range(0,?8,?2.5)?//?[0,?2.5,?5,?7.5]
復(fù)制代碼
基于sendBeacon的安全的數(shù)據(jù)上報(bào)
sendBeacon[24] 異步地向服務(wù)器發(fā)送數(shù)據(jù),同時(shí)不會(huì)延遲頁(yè)面的卸載或影響下一導(dǎo)航的載入性能。
function?report(url,?data)?{
????if?(typeof?navigator.sendBeacon?!==?"function")?{
????????return?console.error("sendBeacon不被支持");
????}
????navigator.sendBeacon(url,?data);
}
復(fù)制代碼
示例
window.addEventListener('unload',?logData,?false);
function?logData()?{
???report("/log",?"被卸載了");
}
復(fù)制代碼
基于toLocaleString千分位
正則?遍歷?不需要的。內(nèi)置函數(shù)就解決。
當(dāng)然,如果是超大的數(shù),可能是會(huì)有問(wèn)題的。
代碼
function?formatMoney(num){
????return?(+num).toLocaleString("en-US");
}
復(fù)制代碼
示例
console.log(formatMoney(123456789));??//?123,456,789
console.log(formatMoney(6781))?//?6,781
console.log(formatMoney(5))?//?5
超大的數(shù)
formatMoney(19999999933333333333333)?//?19,999,999,933,333,333,000,000
復(fù)制代碼
Promise順序執(zhí)行
讓Promise順序的執(zhí)行,并支持初始化參數(shù)和結(jié)果作為參數(shù)傳遞。
代碼
function?runPromises(promiseCreators,?initData)?{
????return?promiseCreators
????????.reduce((promise,?next)?=>?promise
????????????????.then((data)?=>?next(data))
????????????,?Promise.resolve(initData));
}
復(fù)制代碼
示例
var?promise1?=?function?(data?=?0)?{
????return?new?Promise(resolve?=>?{
????????resolve(data?+?1000);
????});
}
var?promise2?=?function?(data)?{
????return?new?Promise(resolve?=>?{
????????resolve(data?-500);
????});
}
runPromises([promise1,?promise2],?1).then(res=>console.log(res));
復(fù)制代碼
延時(shí)執(zhí)行delay
延時(shí)執(zhí)行某函數(shù),且只會(huì)執(zhí)行一次。
代碼
function?delay(fn?=?()?=>?{?},?delay?=?5000,?context?=?null)?{
????let?ticket?=?null;
????let?runned?=?false;
????return?{
????????run(...args)?{
????????????return?new?Promise((resolve,?reject)?=>?{
????????????????if?(runned?===?true)?{
????????????????????return;
????????????????}
????????????????runned?=?true;
????????????????ticket?=?setTimeout(async?()?=>?{
????????????????????try?{
????????????????????????const?res?=?await?fn.apply(context,?args);
????????????????????????resolve(res);
????????????????????}?catch?(err)?{
????????????????????????reject(err)
????????????????????}
????????????????},?delay)
????????????})
????????},
????????cancel:?()?=>?{
????????????clearTimeout(ticket);
????????}
????}
}
復(fù)制代碼
示例
delay(function?()?{
????console.log("你們好");
}).run();
const?{?run,?cancel?}?=?delay(function?(name)?{
????console.log("你好:",?name);
});
run("吉他");
run("吉他");
//?你們好
//?你好:?吉他
復(fù)制代碼
進(jìn)度值映射
進(jìn)度映射,比較只有 10%的進(jìn)度,確要顯示50%的進(jìn)度的場(chǎng)景。
代碼
function?adjustProgress(progress:?number,?mapping:?{?real:?number;?target:?number?}[]?=?[])?{
????if?(progress?<?0)?{
????????return?0;
????}
????if?(!mapping?||?mapping.length?<=?0)?{
????????return?progress;
????}
????//?第一個(gè)
????const?f?=?mapping[0];
????if?(progress?<=?f.real)?{
????????return?progress?*?(f.target?/?f.real);
????}
????//?最后一個(gè)
????const?l?=?mapping[mapping.length?-?1];
????if?(progress?>=?l.target)?{
????????return?l.target;
????}
????const?curIndex?=?mapping.findIndex(m?=>?m.real?>=?progress);
????if?(!curIndex)?{
????????return?progress;
????}
????const?cur?=?mapping[curIndex];
????const?pre?=?mapping[curIndex?-?1];
????//?????原基數(shù)?????+???實(shí)際進(jìn)度/最大實(shí)際進(jìn)度?*?期望間距
????return?pre.target?+?(progress?-?pre.real)?/?(cur.real?-?pre.real)?*?(cur.target?-?pre.target);
}
復(fù)制代碼
示例
const?mapping?=?[{
????real:?0,
????target:?0,
},?{
????real:?30,
????target:?50
},?{
????real:?60,
????target:?80
},?{
????real:?100,
????target:?100
}];
console.log("15",?adjustProgress(15,?mapping));??//?15?25
console.log("25",?adjustProgress(25,?mapping));?//?25?41.66666666666667
console.log("50",?adjustProgress(50,?mapping));?//?50?70
console.log("60",?adjustProgress(60,?mapping));?//?60?80
console.log("100",?adjustProgress(100,?mapping));?//?100?100
復(fù)制代碼
滑滾動(dòng)頁(yè)面到頂部
代碼
PC端滾動(dòng)的根元素是document.documentElement,
移動(dòng)端滾動(dòng)的的根元素是document.body,
有一個(gè)更好的屬性document.scrollingElement能自己識(shí)別文檔的滾動(dòng)元素, 其在PC端等于document.documentElement, 其在移動(dòng)端等于document.body
//?smooth?選項(xiàng)在Safari上支持不好
function?scrollToTop(){
????window.scrollTo({
????????left:?0,
????????top:?0,
????????behavior:?'smooth
????})
}
function?scrollToTop()?{
????let?scrollTop?=?document.documentElement.scrollTop?||?document.body.scrollTop;
????if?(scrollTop?>?0)?{
????????window.requestAnimationFrame(scrollToTop);
????????window.scrollTo(0,?scrollTop?-?scrollTop?/?8);
????}
};
復(fù)制代碼
禁止選擇和復(fù)制
代碼
['contextmenu',?'selectstart',?'copy'].forEach(function(ev){
????document.addEventListener(ev,?function(ev){
????????ev.preventDefault();
????????ev.returnValue?=?false;
????})
});
復(fù)制代碼
當(dāng)然也有CSS方案
body?{
????-moz-user-select:?none;
????-webkit-user-select:?none;
????-ms-user-select:?none;
????-khtml-user-select:?none;
????user-select:?none;
}
復(fù)制代碼
禁止圖片拖拽
代碼
['dragstart'].forEach(function(ev){
????document.addEventListener(ev,?function(ev){
????????ev.preventDefault();
????????ev.returnValue?=?false;
????})
});
復(fù)制代碼
自增長(zhǎng)ID
自己生產(chǎn)自增長(zhǎng)的ID值,當(dāng)然可以更復(fù)雜一些。
代碼
let?id?=?0;
function?getId()?{
????return?id++;
}
復(fù)制代碼
示例
console.log(getId());?//?1
console.log(getId());?//?2
復(fù)制代碼
寫(xiě)在后面
寫(xiě)作不易,你的一贊一在看,就是我前行的最大動(dòng)力。
往期干貨:
?26個(gè)經(jīng)典微信小程序+35套微信小程序源碼+微信小程序合集源碼下載(免費(fèi))
?干貨~~~2021最新前端學(xué)習(xí)視頻~~速度領(lǐng)取
?前端書(shū)籍-前端290本高清pdf電子書(shū)打包下載
點(diǎn)贊和在看就是最大的支持??
