【總結系列】原生JS常用技巧

(一)字符串相關

一、獲取查詢字符串

/**
* 獲取url中的查詢字符串的某個值
* @param key 必須參數,查詢字符串中的一個鍵名
* @param url 可選參數,默認爲當前頁面的url
* @returns {string|null}
*/
function getQuery(key,url=document.location.href){
    const query = url.split("?").length > 1 ? url.split("?")[1] : null
    if(!query)
        throw new Error("當前url沒有查詢字符串")
    const entries = query.split("&")
    for(let i = 0;i < entries.length;i++){
        const entry = entries[i].split("=")
        if(key === entry[0]){
            return entry[1]
        }
    }
    return null
}

二、翻轉字符串

如果需要翻轉一個數組,那麼很簡單,因爲原生的方法就支持實現這個功能。但是,如果需要翻轉一個字符串就需要自己實現了。實現的思路也很簡單,先把字符串轉換爲數組,將數組翻轉,最後將數組轉化爲字符串。

/**
*
* @param str 待翻轉的字符串
* @returns {string} 翻轉後的字符串
*/
function reverseString(str){
    str = "" + str
    return str.split("").reverse().join("")
}

三、去重字符串相同字符

感覺這個需求在面試中出現的比較多,實現原理:

字符串是可迭代對象,所以可以轉換爲 Set 類型,利用 Set 類型元素唯一性進行去重,然後將 Set 轉換爲數組,最後通過數組方法拼接成字符串。

[...new Set(str)].join("")

(二)數組相關

一、數組去重

數組去重是一個很常見的需求了,在網上也有很多去重的方法,在此介紹兩個簡潔的去重方法。

unique(arr){
    if(!Array.isArray(arr))
        throw new Error("arr is not a array")
    return [...new Set(arr)] //或者 Array.from(new Set(arr))
}

上面就是利用了 Set 元素的唯一性。

但是,如果數組中存在內容相同的引用類型,那麼這個方法就沒有效果了。

比如:

let arr = [
	{name:"jonas",age:18},
	{name:"jonas",age:18},
	{name:"jonas",age:18}
]

你會發現無論使用上面的方法還是網絡上介紹的數組去重方法,都實現不了這種去重。所以,這時候自己寫了一個方法用於解決這個問題(見下面)。

二、數組深度去重

對於基本數據類型的去重很簡單,就是上面的一行代碼就解決了,然而複雜的是引用類型。此處的引用類型主要是指對象和數組,所以,問題就變成兩個:①去重內容相同的對象②去重內容相同的數組。

因爲數組和對象的特徵不同,存儲數據的方式也不同,所以第一件事就是將數組和對象區分開來。

/**
 * 獲取一個值的類型
 * @param target
 * @returns {string} 返回這個值的類型。如果是數組,則返回 "Array";是對象返回 "Object"
 */
function getType(target) {
    return Object.prototype.toString.call(target).slice(8,-1)
}

接下來的問題就是判斷兩個對象中的內容是否完全一致了。

/**
 * 判斷兩個對象的內容是否完全一致,返回布爾值
 * @param a
 * @param b
 * @returns {boolean}
 */
function objUnique(a,b) {
    //限制兩個參數都是對象
    if(getType(a) !== 'Object' && getType(b) !== 'Object')
        return false
    //獲取其中一個對象的屬性
    let keyList = Object.keys(a)
    for(let i = 0;i < keyList.length;i++){
        //獲取鍵名
        let propName = keyList[i]
        //處理嵌套
        if(getType(a[propName]) === 'Object' && getType(b[propName]) === 'Object'){
            objUnique(a[propName],b[propName])
        }else if(getType(a[propName]) === 'Array' && getType(b[propName]) === 'Array'){
            arrUnique(a[propName],b[propName])
        }else if(a[propName] !== b[propName]){
            //存在一對鍵值對不同,則表示兩個對象不同
            return false
        }
    }
    return true
}

/**
 * 判斷兩個數組的內容是否完全一致,返回布爾值
 * @param a
 * @param b
 * @returns {boolean}
 */
function arrUnique(a, b) {
    //限制兩個參數都是數組
    if(getType(a) !== 'Array' && getType(b) !== 'Array')
        return false
    //如果長度不等,則兩個數組不可能相同
    if(a.length !== b.length)
        return false
    //處理嵌套
    for(let i = 0;i < a.length;i++){
        if(getType(a[i]) === 'Object' && getType(b[i]) === 'Object'){
            objUnique(a[i],b[i])
        }else if(getType(a[i]) === 'Array' && getType(b[i]) === 'Array'){
            arrUnique(a[i],b[i])
        }else if(a[i] !== b[i]){
            //存在一個元素不同,則表示兩個數組不同
            return false
        }
    }
    return true
}

整合:

/**
 * 數組深度去重
 * @param arr
 * @returns {null|*[]}
 */
function unique(arr) {
    //處理異常參數
    if(getType(arr) !== 'Array')
        throw new Error("arr is not a Array.")
    //基本數據類型容器
    let commonList = []
    //對象收納容器
    let objList = []
    //數組收納容器
    let arrList = []

    //數據分類
    arr.forEach(item => {
        if(getType(item) === 'Object'){
            objList.push(item)
        }else if(getType(item) === 'Array'){
            arrList.push(item)
        }else{
            commonList.push(item)
        }
    })
    //基本數據類型去重
    commonList = Array.from(new Set(commonList))
    //對象收納容器去重
    for(let i = 0;i < objList.length;i++){
        for(let j = i + 1;j < objList.length;j++){
            if(objUnique(objList[i],objList[j])){
                objList.splice(j,1)
                j--
            }
        }
    }
    //數組收納容器去重
    for(let i = 0;i < arrList.length;i++){
        for(let j = i + 1;j < arrList.length;j++){
            if(arrUnique(arrList[i],arrList[j])){
                arrList.splice(j,1)
                j--
            }
        }
    }
    //合併收納容器
    return [...commonList,...objList,...arrList]
}

三、數組的並集,交集,差集

公共代碼:

let arr1 = [1, 2, 3]
let arr2 = [4, 3, 2]

①並集

let arr = [...new Set([...arr1,...arr2])]

②交集

let arr = arr1.filter(x => arr2.includes(x))

③差集

let arr = arr1.filter(x => !arr2.includes(x))

四、取數組中的最大值與最小值

/**
* 取數組中的最大值與最小值,忽略非數值元素和NaN
* @param arr
* @return {{min: *, max: *}|null}
*/
function minAndMax(arr) {
    //規範類型
    function getType(obj) {
        return Object.prototype.toString.call(obj).slice(8,-1)
    }
    if(getType(arr) !== "Array")
        throw new Error("arr is not array")
    let min
    let max
    let flag = 0
    for(let i = 0;i < arr.length;i++){
        let current = arr[i]
        //剔除非法數據
        if(getType(current) !== "Number" || Number.isNaN(current)){
            continue
        }
        if(!flag){
            min = current
            max = current
            flag++
        }
        if(current > max)
            max = current
        if(current < min)
            min = current
    }
    return {min,max}
}

五、數組的扁平化

扁平化,就是將多維數組轉換爲一維數組。

ES6 中有提供了一個方法用於扁平化:

let arr = [1,[2,[3,[4]]]]
let result = arr.flat(Infinity) //參數代表扁平層數,默認爲1

(三)對象相關

一、對象或數組的深拷貝

JSON.parse(JSON.stringify(obj))

(四)數據類型相關

一、準確檢測數據類型

無論是 typeof 還是 instanceof ,都無法很好的準確的檢測一個未知對象的數據類型。所以還是手寫了一個方法:

/**
 * 檢測數據類型
 * @param obj 待檢測對象
 * @returns {string} 返回具體的類型(首字母大寫)
 */
function checkType(obj) {
    return Object.prototype.toString.call(obj).slice(8,-1)
}
  • 如果是對象,則返回 Object;
  • 如果是數組,則返回 Array
  • 如果是字符串,則返回 String

以此類推,該方法還支持 ES6+ 的所有數據類型。

二、數據類型轉換

基本類型的轉換就不再此處說了,這裏說的引用類型之間的轉換。

Map 轉換爲普通對象

/**
 * 將map轉換爲普通對象
 * @param map
 * @return obj
 */
function mapToObj(map) {
    if(getType(map) !== "Map")
        throw new Error("map is not a Map")
    let obj = {}
    map.forEach((value,key) => {
        if(getType(key) !== "String")
            key = Object.prototype.toString.call(key)
        obj[key] = value
    })
    function getType(obj) {
        return Object.prototype.toString.call(obj).slice(8,-1)
    }
    return obj
}

②對象轉換爲 Map

一行代碼解決:

new Map(Object.entries(obj))

(五)日期相關

一、將日期類型轉換爲yyyy-mm-dd hh:mm:ss 格式

function getDate(date = new Date()) {
  const year = date.getFullYear()
  const month = date.getMonth() + 1 < 10 ? `0${date.getMonth() + 1}` : date.getMonth() + 1
  const day = date.getDate() < 10 ? `0${date.getDate()}` : date.getDate()
  const hours = date.getHours() < 10 ? `0${date.getHours()}` : date.getHours()
  const min = date.getMinutes() < 10 ? `0${date.getMinutes()}` : date.getMinutes()
  const seconds = date.getSeconds() < 10 ? `0${date.getSeconds()}` : date.getSeconds()
  return `${year}-${month}-${day} ${hours}:${min}:${seconds}`
}

(未完,一直更)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章