你需要知道的 14 個常用的 JavaScript 函式
1、確定任意物件的具體型別
眾所周知,JavaScript 中有六種原始資料型別(Boolean、Number、String、Null、Undefined、Symbol)和一個物件資料型別。但是你知道物件資料型別可以細分為很多種子型別嗎?一個物件可能是陣列、函式、map 等,如果我們要獲取物件的具體型別,應該怎麼做呢?
程式碼:
function toRawType (value) {
let _toString = Object.prototype.toString;
let str = _toString.call(value)
return str.slice(8, -1)
}
解釋
ECMAScript 有以下規則:
對於不同的物件,呼叫 Object.prototype.toString () 時會返回不同的結果。
而且,Object.prototype.toString () 的返回值總是‘[object’+‘tag’+‘]’的格式。如果我們只想要中間的標籤,我們可以通過正則表示式或者 String.prototype.slice () 刪除兩邊的字元。
例子:
toRawType(null) // "Null" toRawType(/sdfsd/) //"RegExp"
2、快取函式計算結果
如果有這樣的功能:
function computed(str) {
// Suppose the calculation in the funtion is very time consuming
console.log('2000s have passed')
return 'a result'
}
我們要快取函式操作的結果, 稍後呼叫時,如果引數相同,則不再執行該函式,而是直接返回快取中的結果。我們能做什麼?
程式碼:
function cached(fn){
// Create an object to store the results returned after each function execution.
const cache = Object.create(null);
// Returns the wrapped function
return function cachedFn (str) {
// If the cache is not hit, the function will be executed
if ( !cache[str] ) {
let result = fn(str);
// Store the result of the function execution in the cache
cache[str] = result;
}
return cache[str]
}
}
例子:
3、實現 Array.prototype.map
這是 JavaScript 中一個有用的內建方法,你應該能夠自己實現此功能。
程式碼:
const selfMap = function (fn, context) {
let arr = Array.prototype.slice.call(this)
let mappedArr = Array()
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue;
mappedArr[i] = fn.call(context, arr[i], i, this)
}
return mappedArr
}
Array.prototype.selfMap = selfMap;
4、實現 Array.prototype.filter
這是 JavaScript 中一個有用的內建方法,你應該能夠自己實現此功能。
程式碼:
const selfFilter = function (fn, context) {
let arr = Array.prototype.slice.call(this)
let filteredArr = []
for (let i = 0; i < arr.length; i++) {
if(!arr.hasOwnProperty(i)) continue;
fn.call(context, arr[i], i, this) && filteredArr.push(arr[i])
}
return filteredArr
}
Array.prototype.selfFilter = selfFilter;
5、實現 Array.prototype.some
這是 JavaScript 中一個有用的內建方法,你應該能夠自己實現此功能。
程式碼:
const selfSome = function (fn, context) {
let arr = Array.prototype.slice.call(this)
if(!arr.length) return false
for (let i = 0; i < arr.length; i++) {
if(!arr.hasOwnProperty(i)) continue;
let res = fn.call(context,arr[i],i,this)
if(res)return true
}
return false
}
Array.prototype.selfSome = selfSome;
6、實現 Array.prototype.reduce
這是 JavaScript 中一個有用的內建方法,你應該能夠自己實現此功能。
程式碼:
const selfReduce = function (fn, initialValue) {
let arr = Array.prototype.slice.call(this)
let res
let startIndex
if (initialValue === undefined) {
for (let i = 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue
startIndex = i
res = arr[i]
break
}
} else {
res = initialValue
}
for (let i = ++startIndex || 0; i < arr.length; i++) {
if (!arr.hasOwnProperty(i)) continue
res = fn.call(null, res, arr[i], i, this)
}
return res
}
Array.prototype.selfReduce = selfReduce;
7、實現 Array.prototype.flat
程式碼:
const selfFlat = function (depth = 1) {
let arr = Array.prototype.slice.call(this)
if (depth === 0) return arr
return arr.reduce((pre, cur) => {
if (Array.isArray(cur)) {
return [...pre, ...selfFlat.call(cur, depth - 1)]
} else {
return [...pre, cur]
}
}, [])
}
Array.prototype.selfFlat = selfFlat;
例子:
8、柯里化
柯里化是一種將具有多個引數的函式評估為具有單個引數的函式序列的技術。
換句話說,當一個函式不是一次接受所有引數時,而是接受第一個引數並返回一個新函式,該函式接受第二個引數並返回一個新函式,該函式接受第三個引數,依此類推,直到所有引數都已履行。
那就是我們將函式呼叫 add (1,2,3) 轉換為 add (1)(2)(3) 。通過使用這種技術,可以輕鬆地配置和重用小塊。
為什麼有用?
-
柯里化可以幫助您避免一次又一次地傳遞相同的變數。
-
它有助於建立高階函式,它對事件處理非常有幫助。
-
小部件可以輕鬆配置和重用。
讓我們看一個簡單的新增函式。它接受三個運算元作為引數,並返回所有三個運算元的總和作為結果。
function add(a,b,c){
return a + b + c;
}
你可以用太少(結果奇怪)或太多(多餘的引數被忽略)來呼叫它。
add(1,2,3) --> 6 add(1,2) --> NaN add(1,2,3,4) --> 6 //Extra parameters will be ignored.
如何將現有函式轉換為 curried 版本?
程式碼:
function curry(fn) {
if (fn.length <= 1) return fn;
const generator = (...args) => {
if (fn.length === args.length) {
return fn(...args)
} else {
return (...args2) => {
return generator(...args, ...args2)
}
}
}
return generator
}
9、去抖動
去抖動只不過是減少不必要的耗時計算,以提高瀏覽器效能。在某些情況下,某些功能需要更多時間來執行某個操作。例如,以電子商務網站上的搜尋欄為例。
假設使用者想要獲得 “Tutorix 學習套件”。他在搜尋欄中鍵入產品的每個字元。輸入每個字元後,從瀏覽器到伺服器都會進行一次 Api 呼叫,以獲取所需的產品。由於他想要 “Tutorix 學習套件”,使用者必須從瀏覽器到伺服器進行 17 次 Api 呼叫。
想象一個場景,當數百萬人進行相同的搜尋從而呼叫數十億個 Api 時。所以一次呼叫數十億個 Api 肯定會導致瀏覽器效能變慢。為了減少這個缺點,去抖動出現了。
在這種情況下,去抖動將在兩次擊鍵之間設定一個時間間隔,假設為 2 秒。如果兩次擊鍵之間的時間超過 2 秒,則只會進行 Api 呼叫。在這 2 秒內,使用者可以輸入至少一些字元,從而減少 Api 呼叫的這些字元。由於 Api 呼叫減少,瀏覽器效能將提高。必須注意,每次擊鍵都會更新 Debouncing 功能。
程式碼:
const debounce = (func, time = 17, options = {
leading: true,
context: null
}) => {
let timer;
const _debounce = function (...args) {
if (timer) {
clearTimeout(timer)
}
if (options.leading && !timer) {
timer = setTimeout(null, time)
func.apply(options.context, args)
}else{
timer = setTimeout(() => {
func.apply(options.context, args)
timer = null
}, time)
}
};
_debounce.cancel = function () {
clearTimeout(timer)
timer = null
};
return _debounce
};
10、 節流
節流將以這樣一種方式更改函式,即它可以在一個時間間隔內最多觸發一次。例如,無論使用者單擊按鈕多少次,限制將在 1000 毫秒內僅執行一次該功能。
程式碼:
const throttle = (func, time = 17, options = {
leading: true,
trailing: false,
context: null
}) => {
let previous = new Date(0).getTime()
let timer;
const _throttle = function (...args) {
let now = new Date().getTime();
if (!options.leading) {
if (timer) return
timer = setTimeout(() => {
timer = null
func.apply(options.context, args)
}, time)
} else if (now - previous > time) {
func.apply(options.context, args)
previous = now
} else if (options.trailing) {
clearTimeout(timer)
timer = setTimeout(() => {
func.apply(options.context, args)
}, time)
}
};
_throttle.cancel = () => {
previous = 0;
clearTimeout(timer);
timer = null
};
return _throttle
};
11、 延遲載入圖片
延遲載入圖片意味著在網站上非同步載入圖片 —— 也就是說,在首屏內容完全載入之後,甚至有條件地,只有當它們出現在瀏覽器的視口中時。
這意味著如果使用者不一直向下滾動,則放置在頁面底部的影象甚至不會被載入。
程式碼:
// getBoundingClientRect
let imgList1 = [...document.querySelectorAll(".get_bounding_rect")]
let num = imgList1.length
let lazyLoad1 = (function () {
let count = 0
return function () {
let deleteIndexList = []
imgList1.forEach((img,index) => {
let rect = img.getBoundingClientRect()
if (rect.top < window.innerHeight) {
img.src = img.dataset.src
// Add picture to delete list after loading successfully
deleteIndexList.push(index)
count++
if (count === num) {
//When all pictures are loaded, unbind scroll event
document.removeEventListener('scroll',lazyLoad1)
}
}
})
// Delete loaded pictures
imgList1 = imgList1.filter((_,index)=>!deleteIndexList.includes(index))
}
})()
12、陣列隨機無序
我們經常需要打亂一個數組。
程式碼:
// Randomly select one of all elements after the current element to exchange with the current element
function shuffle(arr) {
for (let i = 0; i < arr.length; i++) {
let randomIndex = i + Math.floor(Math.random() * (arr.length - i));
[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]
}
return arr
}
// Generate a new array, randomly take an element from the original array and put it into the new array
function shuffle2(arr) {
let _arr = []
while (arr.length) {
let randomIndex = Math.floor(Math.random() * (arr.length))
_arr.push(arr.splice(randomIndex, 1)[0])
}
return _arr
}
13、單例模式
單例模式將特定物件的例項數限制為一個,這個單一例項稱為單例模式。
單例在需要從單箇中心位置協調系統範圍的操作的情況下很有用。一個例子是資料庫連線池。池管理整個應用程式的所有資料庫連線的建立、銷燬和生命週期,確保沒有連線 “丟失”。
單例減少了對全域性變數的需求,這在 JavaScript 中尤為重要,因為它限制了名稱空間汙染和相關的名稱衝突風險。
程式碼:
function proxy(func) {
let instance;
let handler = {
construct(target, args) {
if (!instance) {
// Create an instance if there is not exist
instance = Reflect.construct(func,args)
}
return instance
}
}
return new Proxy(func, handler)
}
// example
function Person(name, age) {
this.name = name
this.age = age
}
const SingletonPerson = proxy(Person)
let person1 = new SingletonPerson('zhl', 22)
let person2 = new SingletonPerson('cyw', 22)
console.log(person1 === person2) // true
14、實現 JSON.stringify
這是 JavaScript 中一個有用的內建方法,你應該能夠自己實現此功能。
程式碼:
const isString = value => typeof value === 'string';
const isSymbol = value => typeof value === 'symbol'
const isUndefined = value => typeof value === 'undefined'
const isDate = obj => Object.prototype.toString.call(obj) === '[object Date]'
const isFunction = obj => Object.prototype.toString.call(obj) === '[object Function]';
const isComplexDataType = value => (typeof value === 'object' || typeof value === 'function') && value !== null;
const isValidBasicDataType = value => value !== undefined && !isSymbol(value);
const isValidObj = obj => Array.isArray(obj) || Object.prototype.toString.call(obj) === '[object Object]';
const isInfinity = value => value === Infinity || value === -Infinity
// Symbol,undefined,function in array will become null
// Infinity,NaN will also become null
const processSpecialValueInArray = value =>
isSymbol(value) || isFunction(value) || isUndefined(value) || isInfinity(value) || isNaN(value) ? null : value;
// Handling property values according to JSON specification
const processValue = value => {
if (isInfinity(value) || isNaN(value)) {
return null
}
if (isString(value)) {
return `"${value}"`
}
return value
};
// obj.loop = obj
const jsonStringify = (function () {
// Closure + WeakMap prevent circular references
let wp = new WeakMap();
//It is the function in the closure that recursively calls jsonstrify, not the jsonstrify function declared by const
return function jsonStringify(obj) {
if (wp.get(obj)) throw new TypeError('Converting circular structure to JSON');
let res = "";
if (isComplexDataType(obj)) {
if (obj.toJSON) return obj.toJSON;
if (!isValidObj(obj)) {
return
}
wp.set(obj, obj);
if (Array.isArray(obj)) {
res += "[";
let temp = [];
obj.forEach((value) => {
temp.push(
isComplexDataType(value) && !isFunction(value) ?
jsonStringify(value) :
`${processSpecialValueInArray(value, true)}`
)
});
res += `${temp.join(',')}]`
} else {
res += "{";
let temp = [];
Object.keys(obj).forEach((key) => {
if (isComplexDataType(obj[key])) {
if (isValidObj(obj[key])) {
temp.push(`"${key}":${jsonStringify(obj[key])}`)
} else if (isDate(obj[key])) {
temp.push(`"${key}":"${obj[key].toISOString()}"`)
} else if (!isFunction(obj[key])) {
temp.push(`"${key}":{}`)
}
} else if (isValidBasicDataType(obj[key])) {
temp.push(`"${key}":${processValue(obj[key])}`)
}
});
res += `${temp.join(',')}}`
}
} else if (isSymbol(obj)) {
return
} else {
return obj
}
return res
}
})();
// example
let s = Symbol('s')
let obj = {
str: "123",
arr: [1, {e: 1}, s, () => {
}, undefined,Infinity,NaN],
obj: {a: 1},
Infinity: -Infinity,
nan: NaN,
undef: undefined,
symbol: s,
date: new Date(),
reg: /123/g,
func: () => {
},
dom: document.querySelector('body'),
};
console.log(jsonStringify(obj));
console.log(JSON.stringify(obj));
例子:
總結
以上就是我與你分享的 14 個 JavaScript 的函式,這些函式也是我們作為一名 web 前端開發人員必須要知道的,希望對你有用,如果覺得對你有幫助的話,請記得點讚我,關注我,並將它分享給你身邊做開發的朋友,也許能夠幫助到他。