VUE源碼工具函數分析

  • 源碼中使用了typescript語法 export導出 本地練習時因爲沒有配置環境先註釋掉
// 判斷當前環境
    // 1.1inBrowser: 檢測當前宿主環境是否是瀏覽器
    // 通過判斷 `window` 對象是否存在即可
    // export const inBrowser = typeof window !== 'undefined'
    const inBrowser = typeof window !== 'undefined'
    console.log(inBrowser); // true
    console.log(typeof window); // object

    // 1.2 hasProto:檢查當前環境是否可以使用對象的 __proto__ 屬性
    // 一個對象的 __proto__ 屬性指向了其構造函數的原型
    // 從一個空的對象字面量開始沿着原型鏈逐級檢查。
    // export const hasProto = '__proto__' in {}
    const hasProto = '__proto__' in {}
    console.log(hasProto); // true

    // 2.1 獲取當瀏覽器的user Agent
    // toLowerCase目的是 爲了後續的各種環境檢測
    // export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
    const UA = inBrowser && window.navigator.userAgent.toLowerCase()
    console.log(UA); // mozilla/5.0 (windows nt 10.0; wow64) applewebkit/537.36 (khtml, like gecko) chrome/68.0.3440.106 safari/537.36

    // 2.2 IE瀏覽器判斷
    // 解析:使用正則去匹配 UA 中是否包含'msie'或者'trident'這兩個字符串即可判斷是否爲 IE 瀏覽器
    // export const isIE = UA && /msie|trident/.test(UA)
    const isIE = UA && /msie|trident/.test(UA);
    console.log(isIE);  // false   用的chrome

    // 2.3 IE9| Edge | Chrome 判斷
    export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
    export const isEdge = UA && UA.indexOf('edge/') > 0
    export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge

    // 字符串操作
    // 3.1 isReserved:檢測字符串是否以 $ 或者 _ 開頭
    // charCodeAt() 方法可返回指定位置的字符的 Unicode 編碼
    // export function isReserved (str: string): boolean { // TS語法
    // const c = (str + '').charCodeAt(0)
    // return c === 0x24 || c === 0x5F
    // }
    // 解析: 獲得該字符串第一個字符的unicode,然後與 0x24 和 0x5F 作比較。
    function isReserved (str) {
    const c = (str + '').charCodeAt(0)
    return c === 0x24 || c === 0x5F
    }
    console.log(isReserved('_abc')); // true
    console.log(isReserved('$abc')); // true
    console.log(isReserved('abc')); // false

    // 3.2 camelize: 連字符轉駝峯
    // const camelizeRE = /-(\w)/g
    // export const camelize = cached((str: string): string => {
    // return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
    // })
    //解析: 定義正則表達式:/-(\w)/g,用來全局匹配字符串中 中橫線及連字符後的一個字符。
    //若捕獲到,則將字符以toUpperCase大寫替換,否則以''替換。 如:camelize('aa-bb') // aaBb
    const camelizeRE = /-(\w)/g
    const camelize = cached((str) => {
    return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
    })
    console.log(camelize('abc-asd')); // cached暫未定義
    // hyphenate:駝峯轉連字符
    const hyphenateRE = /\B([A-Z])/g
    export const hyphenate = cached((str: string): string => {
    return str.replace(hyphenateRE, '-$1').toLowerCase()
    })


    // 3.3 toString: 將給定變量的值轉換爲 string 類型並返回
    // export function toString (val: any): string {
    // return val == null
    //     ? ''
    //     : typeof val === 'object'
    //     ? JSON.stringify(val, null, 2) // 使用2個空格縮進
    //     : String(val)
    // }
    function toString (val) {
    return val == null
        ? ''
        : typeof val === 'object'
        ? JSON.stringify(val, null, 2) // 使用2個空格縮進
        : String(val)
    }
    console.log(toString({})) // '{}'
    console.log(toString(null)) // ''
    console.log(toString(123)) // '123'

    // 3.3.2 mergeHook: 合併生命週期選項
    // 多元運算符的運用 換行對其 方便讀寫
    function mergeHook (
    parentVal: ?Array<Function>,
    childVal: ?Function | ?Array<Function>
    ): ?Array<Function> {
    return childVal
        ? parentVal
            ? parentVal.concat(childVal)
            : Array.isArray(childVal)
                ? childVal
                : [childVal]
        : parentVal
    }

    // 3.4 capitalize:首字符大寫
    // 忽略cached
    export const capitalize = cached((str: string): string => {
    return str.charAt(0).toUpperCase() + str.slice(1)
    })

    // 4. 類型判斷
    // 4.1 isPrimitive: 判斷變量是否爲原型類型
    export function isPrimitive (value: any): boolean %checks {
    return (
        typeof value === 'string' ||
        typeof value === 'number' ||
        // $flow-disable-line
        typeof value === 'symbol' ||
        typeof value === 'boolean'
    )
    }

    // 4.2 isRegExp: 判斷變量是否爲正則對象。
    // 使用 Object.prototype.toString 與 '[object RegExp]' 做全等對比。
    export function isRegExp (v: any): boolean {
    return _toString.call(v) === '[object RegExp]'
    }

    // 4.3 isValidArrayIndex: 判斷變量是否含有效的數組索引
    export function isValidArrayIndex (val: any): boolean {
    const n = parseFloat(String(val))
    // n >= 0 && Math.floor(n) === n 保證了索引是一個大於等於 0 的整數
    return n >= 0 && Math.floor(n) === n && isFinite(val)
    }

    // 4.4 isObject: 區分對象和原始值
    export function isObject (obj: mixed): boolean %checks {
    return obj !== null && typeof obj === 'object'
    }

    // 5.Vue中的閉包使用
    // 5.1 makeMap():判斷一個變量是否包含在傳入字符串裏
    export function makeMap (
    str: string,
    expectsLowerCase?: boolean
    ): (key: string) => true | void {
    const map = Object.create(null)
    const list: Array<string> = str.split(',')
    for (let i = 0; i < list.length; i++) {
        map[list[i]] = true
    }
    return expectsLowerCase
        ? val => map[val.toLowerCase()]
        : val => map[val]
    }
    // 定義一個對象map
    // 將 str 分隔成數組並保存到 list 變量中 遍歷list,並以list中的元素作爲 map 的 key,將其設置爲 true
    // 返回一個函數,並且如果expectsLowerCase爲true的話,小寫map[key]:
    let isLaugh = makMap('嘻嘻,哈哈',true); 
    //設定一個檢測是否爲我的名字的方法,第二個參數不區分大小寫
    isLaugh('嘻嘻')  // true
    isLaugh('哈哈')  // true
    isLaugh('呵呵')  // false

    // 5.2 once:只調用一次的函數
    // 以called作爲回調標識符。調用此函數時,called標示符改變,下次調用就無效了
    export function once (fn: Function): Function {
    let called = false
    return function () {
        if (!called) {
        called = true
        fn.apply(this, arguments)
        }
    }
    }

    // 5.3 cache:創建一個緩存函數
    /**
    * Create a cached version of a pure function.
    */
    export function cached<F: Function> (fn: F): F {
    const cache = Object.create(null)
    return (function cachedFn (str: string) {
        const hit = cache[str]
        return hit || (cache[str] = fn(str))
    }: any)
    }
    // const cache = Object.create(null)創建純函數是爲了防止變化(純函數的特性:輸入不變則輸出不變)。

    // 多類型的全等判斷
    // looseEqual: 判斷兩個值是否相等  結合註釋很容易看懂
    export function looseEqual (a: any, b: any): boolean {
    // 當 a === b 時,返回true
    if (a === b) return true
    // 否則進入isObject判斷
    const isObjectA = isObject(a)
    const isObjectB = isObject(b)
    // 判斷是否都爲Object類型
    if (isObjectA && isObjectB) {
        try {
        // 調用 Array.isArray() 方法,再次進行判斷
        // isObject 不能區分是真數組還是對象(typeof)
        const isArrayA = Array.isArray(a)
        const isArrayB = Array.isArray(b)
        // 判斷是否都爲數組
        if (isArrayA && isArrayB) {
            // 對比a、bs數組的長度
            return a.length === b.length && a.every((e, i) => {
            // 調用 looseEqual 進入遞歸
            return looseEqual(e, b[i])
            })
        } else if (!isArrayA && !isArrayB) {
            // 均不爲數組,獲取a、b對象的key集合
            const keysA = Object.keys(a)
            const keysB = Object.keys(b)
            // 對比a、b對象的key集合長度
            return keysA.length === keysB.length && keysA.every(key => {
            //長度相等,則調用 looseEqual 進入遞歸
            return looseEqual(a[key], b[key])
            })
        } else {
            // 如果a、b中一個是數組,一個是對象,直接返回 false
            /* istanbul ignore next */
            return false
        }
        } catch (e) {
        /* istanbul ignore next */
        return false
        }
    } else if (!isObjectA && !isObjectB) {
        return String(a) === String(b)
    } else {
        return false
    }
    }




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