读Vue3源码,工具函数

746 阅读5分钟

参照教程资料

开始

/* 

  Object.freeze({}) 冻结对象, 第一层对象的值,是不能修改的 

  __DEV__ 就是process.env.NODE_ENV !== 'production'

*/
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
  ? Object.freeze({})
  : {}
export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
/* 空函数,
1, 方便做条件判断; 

2, 方便压缩, function(){} 不方便压缩代码

*/
export const NOOP = () => {}
/**
 * Always return false.
 */
/* 永远返回FALSE的函数,也是方便压缩 */
export const NO = () => false
/* 
  判断字符串是不是on开头,并且on后面的不是小写字母
  isOn('onChange'); // true
  isOn('onchange'); // false
  isOn('on3change'); // true
  https://juejin.cn/post/6844903501034684430
*/
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)
/* 
  判断字符串是不是以onUpdate:开头
  监听器,
  字符串函数, startsWith,这是字符串的新增方法
*/
export const isModelListener = (key: string) => key.startsWith('onUpdate:')
/* 
  对象的合并方法
*/
export const extend = Object.assign
/* 
  这个方法封装的好呀
  移除数组中的一项
  使用splice确实比较消耗性能,
  axios拦截器为了解决这个问题,把要删除的这一项设置为null就行了
  在学习算法的时候,也学到了这个知识
*/
export const remove = <T>(arr: T[], el: T) => {
  const i = arr.indexOf(el)
  if (i > -1) {
    arr.splice(i, 1)
  }
}
/* 
  是不是本身所拥有的属性
  不是通过原型链向上查找的
*/
const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
  val: object,
  key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
/* 
  判断数组
*/
export const isArray = Array.isArray
/* 

判断是不是Map对象 

ES6 提供了 Map 数据结构。
它类似于对象,也是键值对的集合,
但是“键”的范围不限于字符串,
各种类型的值(包括对象)都可以当作键。
也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,
是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。

*/
export const isMap = (val: unknown): val is Map<any, any> =>
  toTypeString(val) === '[object Map]'
/* 
  判断是不是Set对象
  类似于数组,但是成员的值都是唯一的,没有重复
*/
export const isSet = (val: unknown): val is Set<any> =>
  toTypeString(val) === '[object Set]'
  /* 
    判断是不是Date对象
    主要是这个的使用instanceof 的使用
    简单理解成,左边是右边的实例, 原理是根据原型链,向上查找
  */
export const isDate = (val: unknown): val is Date => val instanceof Date
/* 
  判断是不是函数,
  主要是typeof的使用
*/
export const isFunction = (val: unknown): val is Function =>
  typeof val === 'function'
/* 
  判断是不是字符串
*/
export const isString = (val: unknown): val is string => typeof val === 'string'
/* 
  判断是不是Symbol类型的值
  ES6引入的新的原始类型,表示独一无二的值
*/
export const isSymbol = (val: unknown): val is symbol => typeof val === 'symbol'
/* 
  判断是不是对象,
  使用typeof null 也是object
  但是这里如果是数组,也是返回TRUE的 
*/
export const isObject = (val: unknown): val is Record<any, any> =>
  val !== null && typeof val === 'object'
  /* 
    判断是不是Promise
    http://liubin.org/promises-book/  promise迷你书
    主要是异步编程
  */
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
  return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}
/* 
  对象转成字符串
  toTypeString 对象转字符串
  call 是一个函数,里面第一个参数,是执行函数里面this的指向
*/
export const objectToString = Object.prototype.toString
export const toTypeString = (value: unknown): string =>
  objectToString.call(value)
/* 
  学到了,这个挺好的,封装一下,可以直接判断是不是等于 'Array'
  比如, toRawType('');  // 'String' 方便了很多,不用每次都写很长的计算了
*/
export const toRawType = (value: unknown): string => {
  // extract "RawType" from strings like "[object RawType]"
  return toTypeString(value).slice(8, -1)
}
/* 
  判断是不是一个纯粹的对象
*/
export const isPlainObject = (val: unknown): val is object =>
  toTypeString(val) === '[object Object]'
/* 
  判断是不是数字类型的字符串key
  parseInt 注意,第二个参数是进制,设置为10 的意思是,把key认为是10进制,生成正常的10进制数
  常用parseInt(key, 2); 把一个二进制字符串转化为 10进制的数字
  const b = 21;
  b.toString(2); // 10进制转化为二进制
*/
export const isIntegerKey = (key: unknown) =>
  isString(key) &&
  key !== 'NaN' &&
  key[0] !== '-' &&
  '' + parseInt(key, 10) === key
  /* 
    isReservedProp('onVnodeBeforeMount'); // true 
    是否是保留属性
  */
export const isReservedProp = /*#__PURE__*/ makeMap(
  // the leading comma is intentional so empty string "" is also included
  ',key,ref,' +
    'onVnodeBeforeMount,onVnodeMounted,' +
    'onVnodeBeforeUpdate,onVnodeUpdated,' +
    'onVnodeBeforeUnmount,onVnodeUnmounted'
)
/* 
  缓存函数
  这里一直不是很明白这一块
*/
const cacheStringFunction = <T extends (str: string) => string>(fn: T): T => {
  const cache: Record<string, string> = Object.create(null)
  return ((str: string) => {
    const hit = cache[str]
    return hit || (cache[str] = fn(str))
  }) as any
}
/* 
  \w 代表数字,大小写字母,下划线组成
  () 代表 分组捕获

*/
const camelizeRE = /-(\w)/g
/**
 * @private
 */
/* 
    camelize 函数 表示 连字符 转驼峰
    on-click 变成 onClick
*/
export const camelize = cacheStringFunction((str: string): string => {
  return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
})

/* 
  \B 就是指边界,也就是说hyphenateRE这个正则找到大写字母的边界
  hyphenate 函数的作用就是 驼峰 转 连字符 onClick --> on-click
*/
const hyphenateRE = /\B([A-Z])/g
/**
 * @private
 */
/* 
 */
export const hyphenate = cacheStringFunction((str: string) =>
  str.replace(hyphenateRE, '-$1').toLowerCase()
)
/**
 * @private
 */
/* 字符串 ,首字母大写 */
export const capitalize = cacheStringFunction(
  (str: string) => str.charAt(0).toUpperCase() + str.slice(1)
)

/**
 * @private
 */
/* click 转为 onClick */
export const toHandlerKey = cacheStringFunction((str: string) =>
  str ? `on${capitalize(str)}` : ``
)
/* 
  Object.is 这个很有意思, 是ES6 新的语法
  作用是比较两个值是否是严格相等,用于watch检测,值有没有变化

*/
// compare whether a value has changed, accounting for NaN.
export const hasChanged = (value: any, oldValue: any): boolean =>
  !Object.is(value, oldValue)
  /* 
    invokeArrayFns: 执行数组里的函数
    方便统一执行多个函数
  */
export const invokeArrayFns = (fns: Function[], arg?: any) => {
  for (let i = 0; i < fns.length; i++) {
    fns[i](arg)
  }
}
/* 
  Object.defineProperty 是一个很重要的API
  value, 当试图获取属性时,返回的值
  writable, 该属性是否可写
  enumerable, 表示 在for in 中是否会被枚举
  configuarable, 表示是否能被删除
  set 该属性更新操作时候,所调用的函数
  get 该属性,获取的时候,调用的函数

  除了value的默认值是undefined之外,别的默认值都是FALSE
*/
/* 
  所以说,def函数的作用是,定义一个可以删除,但是不能改变和枚举的key,value
*/
export const def = (obj: object, key: string | symbol, value: any) => {
  Object.defineProperty(obj, key, {
    configurable: true,
    enumerable: false,
    value
  })
}
/* 
  Number.isNaN(NaN); // true
  转化为数字
*/
export const toNumber = (val: any): any => {
  const n = parseFloat(val)
  return isNaN(n) ? val : n
}

/* 
  全局对象 
  global 指的node环境下
  第一次这个_globalThis为undefined
  第二次就是正常的那个值了
  这个写法很棒
*/
let _globalThis: any
export const getGlobalThis = (): any => {
  return (
    _globalThis ||
    (_globalThis =
      typeof globalThis !== 'undefined'
        ? globalThis
        : typeof self !== 'undefined'
        ? self
        : typeof window !== 'undefined'
        ? window
        : typeof global !== 'undefined'
        ? global
        : {})
  )
}

总结

2021.11.21

学到了一些新的API,比如 Object.is() 之前确实没用过

关于, 缓存函数这一块,理解的还是不够透彻