【若川视野 x 源码共读】第24期 | vue2工具函数

121 阅读2分钟

1.说明

本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。

2.前言

本文是针对【若川视野 x 源码共读】第24期 | vue2工具函数的阅读总结笔记,该文并不会全部阐述文章中工具函数的使用,只会针对个别本人觉得复杂或者理解不透,亦或是比较耐用的工具函数,进行些许笔墨的分析。如有描述不对、不足的地方,还请及时告知,不吝感谢!

3.工具函数

emptyObject

var emptyObject = Object.freeze({})
复制代码

isUndef

function isUndef (v) {
  return v === undefined || v === null
}
复制代码

isDef

  • 是否已定义
function isDef (v) {
  return v !== undefined && v !== null
}
复制代码

isPrimitive

  • 是否是原始值
  • 原始值包括 字符串 数字 symbol 布尔
function isPrimitive (value) {
  return (
    typeof value === 'string' ||
    typeof value === 'number' ||
    // $flow-disable-line
    typeof value === 'symbol' ||
    typeof value === 'boolean'
  )
}
复制代码

toRawType

  • 转换成原始类型

isPlainObject

  • 是否是纯对象
var _toString = Object.prototype.toString;

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

console.log(isRawType([1,2])); // Array
console.log(isRawType('hello')); // String
console.log(isRawType(false)); // Boolean
console.log(isRawType(null)); // Null
console.log(isRawType({name: 'hello'})); // Object

function isPlainObject (obj) {
  return _toString.call(obj) === '[object Object]'
}
// 区分 Array 和 Object
console.log(isPlainObject([])); // false
console.log(isPlainObject({})); // true
复制代码

isPromise

  • 是否 Promise
function isPromise (val) {
  return (
    isDef(val) &&
    typeof val.then === 'function' &&
    typeof val.catch === 'function'
  )
}

console.log(isPromise('hello')); // false
console.log(isPromise(new Promise((resolve, reject) => {
  resolve('success')
}))); // true
async function fn() {}
console.log(isPromise(fn())); // true
复制代码

makeMap

  • 传入以逗号(,)分割的字符串,生成一个 map(key, value),返回一个函数用来检测 key 是否在这个 map 中
function makeMap (
  str,
  expectsLowerCase
) {
  var map = Object.create(null);
  var list = str.split(',');
  for (var i = 0; i < list.length; i++) {
    map[list[i]] = true;
  }
  return expectsLowerCase
    ? function (val) { return map[val.toLowerCase()]; }
    : function (val) { return map[val]; }
}

const fn1 = makeMap('name,age')
console.log(fn1('name')); // true
console.log(fn1('xx')); // undefined
复制代码

hasOwn

  • 检测属性是否在自身对象上,而不是查找原型链
var hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn (obj, key) {
  return hasOwnProperty.call(obj, key)
}

const obj1 = {
  name: 'hello',
  age: 12
}

Object.prototype.sex = 1

console.log(hasOwn(obj1, 'name')); // true
console.log(hasOwn(obj1, 'sex')); // false
console.log(obj1.sex); // 1
复制代码

cached

  • 缓存fn
/**
 * Create a cached version of a pure function.
 */
function cached (fn) {
  var cache = Object.create(null);
  return (function cachedFn (str) {
    var hit = cache[str];
    return hit || (cache[str] = fn(str))
  })
}
复制代码

camelize

  • 连字符 - 转小驼峰
var camelizeRE = /-(\w)/g;
var camelize = cached(function (str) {
  return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })
});

console.log(camelize('on-click')); // onClick
复制代码

capitalize

  • 首字母大写
var capitalize = cached(function (str) {
  return str.charAt(0).toUpperCase() + str.slice(1)
});
console.log(capitalize('hello')); // 'Hello'
复制代码

hyphenate

  • 小驼峰转连字符 -
var hyphenateRE = /\B([A-Z])/g;
var hyphenate = cached(function (str) {
  return str.replace(hyphenateRE, '-$1').toLowerCase()
});

console.log(hyphenate('onClick')); // on-click
复制代码

bind

function polyfillBind (fn: Function, ctx: Object): Function { // 实现bind
  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
}

function nativeBind (fn: Function, ctx: Object): Function { // 原生bind
  return fn.bind(ctx)
}

// 兼容bind
export const bind = Function.prototype.bind
  ? nativeBind
  : polyfillBind
复制代码

简单来说就是兼容了老版本浏览器不支持原生的 bind 函数。同时兼容写法,对参数的多少做出了判断,使用callapply实现,据说参数多适合用 apply,少用 call 性能更好。 ?????

toArray

export function toArray (list: any, start?: number): Array<any> { // 伪数组转为真实数组
  start = start || 0
  let i = list.length - start
  const ret: Array<any> = new Array(i)
  while (i--) {
    ret[i] = list[i + start]
  }
  return ret
}
复制代码

extend

export function extend (to: Object, _from: ?Object): Object { // 扩展集成(对象合并)
  for (const key in _from) {
    to[key] = _from[key]
  }
  return to
}
复制代码

toObject

export function toObject (arr: Array<any>): Object { // 把对象数组中的对象提取到一个对象里面
  const res = {}
  for (let i = 0; i < arr.length; i++) {
    if (arr[i]) {
      extend(res, arr[i])
    }
  }
  return res
}
复制代码

noop

export function noop (a?: any, b?: any, c?: any) {} // 空函数,初始化赋值
复制代码

no

export const no = (a?: any, b?: any, c?: any) => false // 永远返回false
复制代码

identity

export const identity = (_: any) => _ //返回值就是入参
复制代码

genStaticKeys

export function genStaticKeys (modules: Array<ModuleOptions>): string { // 合并每个数组的staticKeys项
  return modules.reduce((keys, m) => {
    return keys.concat(m.staticKeys || [])
  }, []).join(',')
}
复制代码

looseEqual

export function looseEqual (a: any, b: any): boolean { // 判断两个内容是否相同,非引用关系的判断
  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])
        })
      } 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
  }
}
复制代码

looseIndexOf

export function looseIndexOf (arr: Array<mixed>, val: mixed): number { // 返回数组元素的下标
  for (let i = 0; i < arr.length; i++) {
    if (looseEqual(arr[i], val)) return i
  }
  return -1
}
复制代码

once

export function once (fn: Function): Function { // 函数执行一次
  let called = false
  return function () {
    if (!called) {
      called = true
      fn.apply(this, arguments)
    }
  }
}

到这里。基础知识就预习结束