天天看點

14 個你必須知道的JavaScript 函數

14 個你必須知道的JavaScript 函數

英文 | https://javascript.plainenglish.io/you-must-understand-these-14-javasript-functions-1f4fa1c620e2

翻譯 | 楊小愛

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 有以下規則:

14 個你必須知道的JavaScript 函數

對于不同的對象,調用 Object.prototype.toString() 時會傳回不同的結果。

14 個你必須知道的JavaScript 函數

而且,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]
  }
}      

例子:

14 個你必須知道的JavaScript 函數

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;      

例子:

14 個你必須知道的JavaScript 函數

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;      

例子:

14 個你必須知道的JavaScript 函數

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;      

例子:

14 個你必須知道的JavaScript 函數

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;      

例子:

14 個你必須知道的JavaScript 函數

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;      

例子:

14 個你必須知道的JavaScript 函數

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
}      

例子:

14 個你必須知道的JavaScript 函數

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 毫秒内僅執行一次該功能。

14 個你必須知道的JavaScript 函數

代碼:

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
}      

例子:

14 個你必須知道的JavaScript 函數

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 個你必須知道的JavaScript 函數

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 函數

總結

以上就是我與你分享的14個JavaScript的函數,這些函數也是我們作為一名web前端開發人員必須要知道的,希望對你有用,如果覺得對你有幫助的話,請記得點贊我,關注我,并将它分享給你身邊做開發的朋友,也許能夠幫助到他。

繼續閱讀