vue源码中的一些工具方法

vue源码中的一些工具方法很实用,在开发的过程中可以用到,做一下记录

一个被冻结的空对象:

const emptyObject = Object.freeze({})

是否是空值、非空置、true、false

function isUndef (v){
  return v === undefined || v === null
}

function isDef (v){
  return v !== undefined && v !== null
}

function isTrue (v) {
  return v === true
}

function isFalse (v) {
  return v === false
}

判断是否为基本类型:

function isPrimitive (value){
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    typeof value === 'symbol' ||
    typeof value === 'boolean'
  )
}

是对象:

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

对类型的判断:

/**
 * Get the raw type string of a value, e.g., [object Object].
 */
const _toString = Object.prototype.toString

function toRawType (value) {
  return _toString.call(value).slice(8, -1)
}

/**
 * 是否是普通对象(排除function、正则、日期这些特殊的对象)
 */function isPlainObject (obj){
  return _toString.call(obj) === '[object Object]'
}
/**
 * 是否是正则对象
 */
function isRegExp (v) { return _toString.call(v) === '[object RegExp]' }

是否是一个有效的索引值

function isValidArrayIndex (val){
  const n = parseFloat(String(val))
  return n >= 0 && Math.floor(n) === n && isFinite(val)
}

是否是promise对象:

function isPromise (val:) {
  return (
    isDef(val) &&
    typeof val.then === 'function' &&
    typeof val.catch === 'function'
  )
}

转化为数字:

function toNumber (val){
  const n = parseFloat(val)
  return isNaN(n) ? val : n
}

删除数组的某一项

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

判断某一属性是私有属性:

const hasOwnProperty = Object.prototype.hasOwnProperty
function hasOwn (obj, key){
  return hasOwnProperty.call(obj, key)
}

生成带有缓存的方法:闭包的一个运用 有缓存就执行缓存的方法,没有缓存,就执行此方法,并且存入缓存

function cached(fn){
  const cache = Object.create(null)
  return (function cachedFn (str) {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  })
}

连字符命名规则转换成驼峰命名规则和将小写字母串转换为开头大写的字符串:

const camelizeRE = /-(\w)/g;//连字符正则
const camelize = cached((str)=> {
  return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})


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

将字符串连接命名法转化为驼峰命名法:

const hyphenateRE = /\B([A-Z])/g;//将驼峰命名法转换为字符串连接符命名规则
const hyphenate = cached((str)=> {
  return str.replace(hyphenateRE, '-$1').toLowerCase()
})

判断两个对象是否相等:

function looseEqual (a, b){
  if (a === b) return true
  const isObjectA = isObject(a)
  const isObjectB = isObject(b)
  if (isObjectA && isObjectB) {
    try {
      const isArrayA = Array.isArray(a)
      const isArrayB = Array.isArray(b)
      if (isArrayA && isArrayB) {
        return a.length === b.length && a.every((e, i) => {//数组长度相等,每一项都相等
          return looseEqual(e, b[i]);//递归
        })
      } else if (a instanceof Date && b instanceof Date) {
        return a.getTime() === b.getTime();//日期时间戳相等
      } else if (!isArrayA && !isArrayB) {//两者都不是数组 就剩下正则、方法、普通对象了
        const keysA = Object.keys(a)
        const keysB = Object.keys(b)
        return keysA.length === keysB.length && keysA.every(key => {
          return looseEqual(a[key], b[key])//key数量一致、并且每个key对应的值相等
        })
      } else {
        /* istanbul ignore next */
        return false
      }
    } catch (e) {
      /* istanbul ignore next */
      return false
    }
  } else if (!isObjectA && !isObjectB) {//都不是引用类型
    return String(a) === String(b)
  } else {
    return false
  }
}

让一个事件或函数只调用一次:

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