成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

盤點(diǎn)Vue源碼中用到的工具函數(shù)

elarity / 3219人閱讀

摘要:用做緩存的高階函數(shù)用高階函數(shù)的好處是無需暴露不同要求的緩存對象在外面,形成一個(gè)閉包。函數(shù)內(nèi)部調(diào)用函數(shù)得到操作后的值,并緩存在對象中,如果再對同一個(gè)值進(jìn)行操作時(shí),則直接從緩存中取,無需再調(diào)用函數(shù)計(jì)算。

以下摘取的函數(shù),在 shared 目錄下公用的工具方法。文件在 util.js 中,githu地址。

提取了一些常用通用的函數(shù)進(jìn)行剖析,主要包含以下內(nèi)容:

創(chuàng)建一個(gè)被凍結(jié)的空對象

判斷是否是 undefinednull

判斷是否不是 undefined 和 null

判斷是否是原始類型

判斷是否是對象類型

判斷有效的數(shù)組下標(biāo)

判斷是否是一個(gè) Promise 對象

刪除數(shù)組中指定元素

用做緩存的高階函數(shù)

遞歸判斷一個(gè)對象是否和另個(gè)一個(gè)對象完全相同

函數(shù)只執(zhí)行一次

自定義 bind 函數(shù)

1. 創(chuàng)建一個(gè)被凍結(jié)的空對象
export const emptyObject = Object.freeze({})

一旦創(chuàng)建不能給這個(gè)對象添加任何屬性。

2. 判斷是否是 undefinednull
function isUndef (v) {
  return v === undefined || v === null
}

在源碼中很多地方會判斷一個(gè)值是否被定義,所以這里直接抽象成一個(gè)公共函數(shù)。
傳入任意值,返回是一個(gè)布爾值。

3. 判斷是否不是 undefinednull
function isDef (v) {
  return v !== undefined && v !== null
}

當(dāng)傳入的值,既不是 undefined 也不是 null 返回true。

4. 判斷是否是原始類型
function isPrimitive (value) {
  return (
    typeof value === "string" ||
    typeof value === "number" ||
    typeof value === "symbol" ||
    typeof value === "boolean"
  )
}

在js中提供了兩大類數(shù)據(jù)類型:

原始類型(基礎(chǔ)類型):String、Number、Boolean、Null、Undefined、Symbol

對象類型:Object、Array、Function

5. 判斷是否是對象類型
function isObject (obj: mixed) {
  return obj !== null && typeof obj === "object"
}

傳入的值排除掉 null,因?yàn)樵趈s中 null 使用運(yùn)算符 typeof 得到的值是 object,這是一個(gè) bug。因?yàn)闅v史原因放棄修復(fù)了。具體可以參考這里查看

6. 判斷有效的數(shù)組下標(biāo)
function isValidArrayIndex (val) {
  const n = parseFloat(String(val)); // 轉(zhuǎn)成數(shù)字
  // 下標(biāo)大于等于0,并且不是小數(shù),并且是有限的數(shù)
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}

可以傳入任意值,先調(diào)用 String 轉(zhuǎn)成字符串,目的是防止傳入的值為 Symbol 類型,那樣直接調(diào)用 parseFloat 會報(bào)錯(cuò),例如:

let test = Symbol("test");
console.log(parseFloat(test))
控制臺捕獲錯(cuò)誤:Uncaught TypeError: Cannot convert a Symbol value to a string

原因是在調(diào)用 parseFloat 時(shí),內(nèi)部會調(diào)用內(nèi)置的 ToString 方法,可以參考這里。而內(nèi)置的 ToString 方法在遇到 Symbol 類型的值時(shí),會拋出 TypeError 錯(cuò)誤,可以參考這里。

跟使用一些隱式轉(zhuǎn)換遇到的問題一樣,例如使用 + 號:

let test = "" + Symbol("text");
控制臺捕獲錯(cuò)誤:Uncaught TypeError: Cannot convert a Symbol value to a string

都是因?yàn)閮?nèi)部會調(diào)用內(nèi)置的 ToString 方法造成的。

而如果手動調(diào)用 toString 方法或者調(diào)用 String,轉(zhuǎn)換為字符串,則不會報(bào)錯(cuò):

let test = Symbol("test");
console.log(test.toString()); // "Symbol(test)"
console.log(String(test)) // "Symbol(test)"

接下來判斷 n >= 0 ,數(shù)組的下標(biāo)不能小于0,這樣就會排除掉小于0的數(shù),以及 NaN

并且 Math.floor(n) === n 一個(gè)數(shù)向下取整并且還等于自己,那只能是正整數(shù),排除掉小數(shù),因?yàn)閿?shù)組的下標(biāo)不能是小數(shù)。

并且用 isFinite 來判定一個(gè)數(shù)字是否是有限數(shù)

console.log(isFinite(Infinity)); // false
console.log(isFinite(-Infinity)); // false
console.log(isFinite(123)); // true
7. 判斷是否是一個(gè) Promise 對象
function isPromise (val) {
  return (
    isDef(val) &&
    typeof val.then === "function" &&
    typeof val.catch === "function"
  )
}

當(dāng)一個(gè)對象存在 then 方法,并且也存在 catch 方法,可以判定為 Promise 對象。

8. 刪除數(shù)組中指定元素

這個(gè)方法有效的避免了進(jìn)行刪除數(shù)組某一項(xiàng)時(shí),都要進(jìn)行查找位置再刪除的重復(fù)工作。

function remove (arr, item){
  if (arr.length) {
    const index = arr.indexOf(item)
    if (index > -1) {
      return arr.splice(index, 1)
    }
  }
}

先判斷數(shù)組長度,如果數(shù)組是空的,則沒必要進(jìn)行刪除操作

indexOf 方法查找到元素在數(shù)組中的位置,如果找到返回元素所在的位置下標(biāo),如果不存在,則返回-1

index>-1 代表存在數(shù)組中,則調(diào)用 splice 進(jìn)行刪除,并返回刪除的元素組成的數(shù)組,也就是 splice 的返回值。

9. 用做緩存的高階函數(shù)

用高階函數(shù)的好處是無需暴露不同要求的緩存對象在外面,形成一個(gè)閉包。下面這個(gè)函數(shù)的技巧,應(yīng)用在工作中,可以提高代碼運(yùn)行的效率。

function cached(fn) {
  // 創(chuàng)建一個(gè)緩存對象
  const cache = Object.create(null)
  return (function cachedFn (str) {
    // 先從緩存對象中找,要操作的值,是否已經(jīng)有了操作結(jié)果
    const hit = cache[str]
    // 如果有,則直接返回;沒有,則調(diào)用函數(shù)對值進(jìn)行操作,并把操作結(jié)果存在緩存對象中
    return hit || (cache[str] = fn(str))
  })
}

調(diào)用 cached 時(shí)會傳入一個(gè) fn 函數(shù),這個(gè)函數(shù)對某些值進(jìn)行操作,操作之后會產(chǎn)生返回值

cached 函數(shù)先定義一個(gè)沒有原型的對象,會比用 {} 高效,因?yàn)椴恍枰^承一大堆 Object.prototype 上的屬性。

執(zhí)行完 cached 會返回一個(gè)函數(shù) cachedFn,將來接收需要操作的值。函數(shù) cachedFn 內(nèi)部調(diào)用 fn 函數(shù)得到操作后的值,并緩存在對象 cache 中,如果再對同一個(gè)值進(jìn)行操作時(shí),則直接從緩存中取,無需再調(diào)用函數(shù)計(jì)算。

例如以下運(yùn)用,函數(shù)的作用是把字符串的首字母大寫。

const capitalize = cached((str) => {
  return str.charAt(0).toUpperCase() + str.slice(1)
})

先調(diào)用 cached 傳入一個(gè)函數(shù),這個(gè)函數(shù)是對字符串進(jìn)行首字母大寫的操作,并返回首字母大寫的字符串結(jié)果,可以說創(chuàng)建了一個(gè)計(jì)算函數(shù)。

cached 的返回值是函數(shù),也就是上面的 cachedFn 函數(shù)。

這時(shí)我們就可以調(diào)用 capitalize 對字符串進(jìn)行首字母大寫了。

capitalize("test");  // "Test"
capitalize("test");  // "Test"
capitalize("test");  // "Test"

第一次調(diào)用 capitalize 函數(shù),先從緩存對象中取值,沒有,則調(diào)用計(jì)算函數(shù)進(jìn)行計(jì)算結(jié)果返回,同時(shí)存入緩存對象中。這時(shí)的緩存對象為:

{test: "Test"}

再多次調(diào)用 capitalize 時(shí),從緩存對象中取值,命中,直接返回,無需再進(jìn)行計(jì)算操作。

10. 遞歸判斷一個(gè)對象是否和另個(gè)一個(gè)對象完全相同

判斷兩個(gè)對象是否相同,主要是判斷兩個(gè)對象包含的值都是一樣的,如果包含的值依然是個(gè)對象,則繼續(xù)遞歸調(diào)用判斷是否相同。

function isObject (obj){
  return obj !== null && typeof obj === "object"
}

function looseEqual (a, b) {
  // 如果是同一個(gè)對象,則相同
  if (a === b) return true
  // 判斷是否是對象
  const isObjectA = isObject(a)
  const isObjectB = isObject(b)
  // 兩者都是對象
  if (isObjectA && isObjectB) {
    try {
      // 判斷是否是數(shù)組
      const isArrayA = Array.isArray(a)
      const isArrayB = Array.isArray(b)
      // 兩者都是數(shù)組
      if (isArrayA && isArrayB) {
        // 長度要一樣,同時(shí)每一項(xiàng)都要相同,遞歸調(diào)用
        return a.length === b.length && a.every((e, i) => {
          return looseEqual(e, b[i])
        })
      } else if (a instanceof Date && b instanceof Date) {  // 如果都是時(shí)間對象,則需要保證時(shí)間戳相同
        return a.getTime() === b.getTime()
      } else if (!isArrayA && !isArrayB) { // 兩者都不是數(shù)組,則為對象
        // 拿到兩者的key值,存入數(shù)組
        const keysA = Object.keys(a)
        const keysB = Object.keys(b)
        // 屬性的個(gè)數(shù)要一樣,遞歸的判斷每一個(gè)值是否相同
        return keysA.length === keysB.length && keysA.every(key => {
          return looseEqual(a[key], b[key])
        })
      } else {  
        return false
      }
    } catch (e) {
      return false
    }
  } else if (!isObjectA && !isObjectB) {  // 兩者都不是對象
    // 轉(zhuǎn)成字符串后,值是否一致
    return String(a) === String(b)
  } else {
    return false
  }
}

判斷兩個(gè)值是否相同,無論是原始類型還是對象類型,如果相同,則直接返回true。

如果兩個(gè)都會對象,則分為兩種情況,數(shù)組和對象。

都是數(shù)組,則保證長度一致,同時(shí)調(diào)用 every 函數(shù)遞歸調(diào)用函數(shù),保證每一項(xiàng)都一樣

是時(shí)間對象,則保證時(shí)間戳相同

是對象,則先取出 key 組成的數(shù)組,兩者 key 的個(gè)數(shù)要相同;再遞歸調(diào)用比較 value 值是否相同

以上都不滿足,直接返回false

如果兩者都不是對象,轉(zhuǎn)成字符串后進(jìn)行比較。

以上都不滿足,直接返回false

例子:

let a1 = [1,2,3,{a:1,b:2,c:[1,2,3]}];
let b1 = [1,2,3,{a:1,b:2,c:[1,2,3]}];
console.log(looseEqual(a1,b1)); // true

let a2 = [1,2,3,{a:1,b:2,c:[1,2,3,4]}];
let b2 = [1,2,3,{a:1,b:2,c:[1,2,3]}];
console.log(looseEqual(a2,b2)); // false
11. 函數(shù)只執(zhí)行一次

同樣利用高階函數(shù),在閉包內(nèi)操作標(biāo)識的真假,來控制執(zhí)行一次。

function once (fn) {
  let called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments)
    }
  }
}

傳入要執(zhí)行一次的函數(shù) fn

設(shè)置標(biāo)識為 false

返回一個(gè)函數(shù)

實(shí)際運(yùn)用:

function test(){
  console.log("我只被執(zhí)行一次");
}
let test2 = once(test);
test2(); // 我只被執(zhí)行一次
test2();
test2();
test2();

調(diào)用 once 函數(shù)后,會返回一個(gè)函數(shù),賦值給 test2

第一次調(diào)用 test2 后,在函數(shù)的尼內(nèi)部,called 初次為 false, 所以可以執(zhí)行函數(shù) test,然后把標(biāo)識 called 設(shè)置為true,就類似關(guān)閉了大門,下次不再執(zhí)行。

之后在調(diào)用 test2 , test 將不再執(zhí)行。

12. 自定義 bind 函數(shù)
function polyfillBind (fn, ctx) {
  function boundFn (a) {
    const l = arguments.length
    return l
      ? l > 1
        ? fn.apply(ctx, arguments)
        : fn.call(ctx, a)
      : fn.call(ctx)
  }

  boundFn._length = fn.length
  return boundFn
}

自定義的 bind 函數(shù)的場景,都是用來兼容不支持原生 bind 方法的環(huán)境。 在自己模擬的 bind 函數(shù)中,實(shí)際上調(diào)用的是 callapply。

這個(gè)方法寫的相對簡單,如果更深入了解,可以戳此查看這篇文章

如有偏差歡迎指正學(xué)習(xí),謝謝。

如果對你有幫助,請關(guān)注【前端技能解鎖】:

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/105346.html

相關(guān)文章

  • 前端每周清單第 34 期:Vue 現(xiàn)狀盤點(diǎn)與 3.0 展望,React 代碼遷移與優(yōu)化,圖片優(yōu)化詳論

    摘要:工程實(shí)踐立足實(shí)踐,提示實(shí)際水平內(nèi)聯(lián)函數(shù)與性能很多關(guān)于性能優(yōu)化的文章都會談及內(nèi)聯(lián)函數(shù),其也是常見的被詬病為拖慢性能表現(xiàn)的元兇之一不過本文卻是打破砂鍋問到底,論證了內(nèi)聯(lián)函數(shù)并不一定就會拖慢性能,過度的性能優(yōu)化反而會有損于應(yīng)用性能。 showImg(https://segmentfault.com/img/remote/1460000011481413?w=1240&h=825); 前端每周...

    CoderStudy 評論0 收藏0
  • 前端每周清單半年盤點(diǎn)之 Angular 篇

    摘要:延伸閱讀學(xué)習(xí)與實(shí)踐資料索引與前端工程化實(shí)踐前端每周清單半年盤點(diǎn)之篇前端每周清單半年盤點(diǎn)之與篇前端每周清單半年盤點(diǎn)之篇 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(ID:frontshow),及時(shí)獲取前端每周清單;本文則是對于半年來發(fā)布的前端每周清單...

    LeviDing 評論0 收藏0
  • 前端每周清單半年盤點(diǎn)之 PWA 篇

    摘要:前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開發(fā)教程工程實(shí)踐深度閱讀開源項(xiàng)目巔峰人生等欄目。 前端每周清單專注前端領(lǐng)域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱點(diǎn)、開發(fā)教程、工程實(shí)踐、深度閱讀、開源項(xiàng)目、巔峰人生等欄目。歡迎關(guān)注【前端之巔】微信公眾號(ID:frontshow),及時(shí)獲取前端每周清單;本文則是對于...

    崔曉明 評論0 收藏0
  • 基于Vue2全家桶的移動端AppDEMO實(shí)現(xiàn)

    好久沒更新過Vue的小文章,上次做了一個(gè)基于Vue+Mint-ui的移動端AppDemo,集成了推送功能,然后通過cordova打包生成apk,移動端表現(xiàn)還不錯(cuò),今天把這個(gè)小東西分享出來,希望有更多的小伙伴能夠用Vue去做一些有意思的東西,本人才疏學(xué)淺,有說的不對的地方,還請大家多多指教。下面按照慣例放上demo地址和源碼地址,希望大家能給我點(diǎn)下star:Demo(進(jìn)去需要先注冊才能登錄,用的lo...

    Jiavan 評論0 收藏0

發(fā)表評論

0條評論

閱讀需要支付1元查看
<