前言
今天我们来聊聊vue3中封装的一些工具类函数。
通过观摩学习也有助于开阔自己的一些思维想法,顺便也加深自己的对于js一些属性方法的使用特性。
源码文件目录:packages/shared/src/index.ts
1、EMPTY_OBJ: 空对象,dev环境的话是冻结的空对象
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__ ? Object.freeze({}) : {}
Object.freeze(obj):冻结一个对象,不可修改、删除、新增。详细文档说明Object.isFrozen(obj):判断一个对象是否被冻结,返回true、false
如果Object.freeze的对象的某个属性还是object的话,那这个属性里面某个字段还是可以修改的。
简单点说:Object.freeze只能冻结对象的第一层。
2、EMPTY_ARR: 空数组,dev环境的话是冻结的空数组
export const EMPTY_ARR = __DEV__ ? Object.freeze([]) : []
3、NOOP: 空函数
export const NOOP = () => {}
作用:方便判断、压缩代码
如果是function(){},就不方便压缩
4、NO: 永远返回false的函数
export const NO = () => false
作用:方便压缩代码
5、isOn: 判断是否以on开头,并且on后面首字母不可以是a-z的小写字母
const onRE = /^on[^a-z]/
export const isOn = (key: string) => onRE.test(key)
isOn('on123') // true
isOn('onClick') // true
isOn('onclick') // false
^: 以什么什么开头^on: 以on开头[^]: 排除^后面的跟的逻辑[^a-z]:不包含a-z的小写字母
详细正则表达式可参考:JS正则表达式完整版
6、isModelListener: 监听器,判断是否以:onUpdate: 开头
export const isModelListener = (key: string) => key.startsWith('onUpdate:')
startsWith:是否以一个给定的字符串开头,返回true、falseendsWith:是否以一个给定的字符串结尾,返回true、false
7、extend: 合并
export const extend = Object.assign
Object.assign合并的时候,会影响第一个参数的数据,例:
const person = {
name: '张三',
age: 25,
family: {sister: '张姐姐'}
}
const person2 = {
name: '李四',
family: {
brother: '李弟弟'
}
}
const person3 = Object.assign(person, person2)
console.log(person) // { name: '李四', age: 25, family: { brother: '李弟弟' } }
console.log(person2) // { name: '李四', family: { brother: '李弟弟' } }
console.log(person3) // { name: '李四', age: 25, family: { brother: '李弟弟' } }
通过上面示例,我们发现person和person3数据都变成一样了,person2没变化。
8、remove: 删除数组的某一项
export const remove = <T>(arr: T[], el: T) => {
const i = arr.indexOf(el)
if (i > -1) {
arr.splice(i, 1)
}
}
9、hasOwn: 判断是不是自己本身所拥有的属性,会忽略掉那些从原型链上继承到的属性
const hasOwnProperty = Object.prototype.hasOwnProperty
export const hasOwn = (
val: object,
key: string | symbol
): key is keyof typeof val => hasOwnProperty.call(val, key)
上面代码用了Object.prototype.hasOwnProperty.call,为什么不直接 xx.hasOwnProperty呢?
__proto__是浏览器自己实现的原型写法- JavaScript 并没有保护 hasOwnProperty 这个属性名,因此,当某个对象可能自有一个占用该属性名的属性时,就需要使用外部的 hasOwnProperty 获得正确的结果。例:
({}).hasOwnProperty.call(foo, 'bar');// 但是这种方式会新建一个对象Object.prototype.hasOwnProperty.call(foo, 'bar');// 这种最优,不会新建对象- 详细文档说明
10、isArray: 判断是否是数组
export const isArray = Array.isArray
11、isMap: 判断是否Map对象
export const isMap = (val: unknown): val is Map<any, any> => toTypeString(val) === '[object Map]'
Map: 类似于对象,也是键值对集合,但是Map的键可以是任意类型
Map和WeakMap的区别:
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。WeakMap的key所引用的对象都是弱引用,只要对象的其他引用被删除,垃圾回收机制就会释放该对象占用的内存,从而避免内存泄漏。- 由于
WeakMap的成员随时可能被垃圾回收机制回收,成员的数量不稳定,所以没有size属性。 - 没有
clear()方法 - 不能遍历(即没有
keys()、values()和entries()方法)
12、isSet: 判断是否Set集合
export const isSet = (val: unknown): val is Set<any> => toTypeString(val) === '[object Set]'
Set:类似于数组,但是每一项的值都是唯一的。
所以我们可以利用这个特性做数组去重:Array.from(new Set([1,2,3,4,1,3]))
Set和WeakSet的区别:
WeakSet的成员只能是对象,而不能是其他类型的值。WeakSet中的对象都是弱引用,其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存WeakSet没有size属性WeakSet不能遍历
13、isDate: 判断是否Date对象
export const isDate = (val: unknown): val is Date => val instanceof Date
14、isFunction: 判断是否是函数
export const isFunction = (val: unknown): val is Function => typeof val === 'function'
15、isString: 判断是否是字符串
export const isString = (val: unknown): val is string => typeof val === 'string'
16、isSymbol: 判断是否为Symbol
export const isSymbol = (val: unknown): val is symbol => typeof val === 'symbol'
17、isObject: 判断是否为对象
export const isObject = (val: unknown): val is Record<any, any> =>
val !== null && typeof val === 'object'
18、isPromise: 判断是否Promise
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}
19、toTypeString: 对象转字符串
export const objectToString = Object.prototype.toString
export const toTypeString = (value: unknown): string => objectToString.call(value)
例:
Object.prototype.toString.call('sdf') // '[object String]'
Object.prototype.toString.call(123) // '[object Number]'
Object.prototype.toString.call(null) // '[object Null]'
Object.prototype.toString.call(undefined) // '[object Undefined]'
Object.prototype.toString.call(()=>{}) // '[object Function]'
Object.prototype.toString.call({}) // '[object Object]'
原理可参考:详细文档说明
20、toRawType: 获取原始类型的字符串
export const toRawType = (value: unknown): string => {
// extract "RawType" from strings like "[object RawType]"
return toTypeString(value).slice(8, -1)
}
21、isPlainObject: 是否是原始类型的Object对象
export const isPlainObject = (val: unknown): val is object =>
toTypeString(val) === '[object Object]'
22、isIntegerKey: 是否是整数类型的字符串键
export const isIntegerKey = (key: unknown) =>
isString(key) &&
key !== 'NaN' &&
key[0] !== '-' &&
'' + parseInt(key, 10) === key
23、isReservedProp: 是否是保留关键字符,类似于ECMA的保留关键字符的作用
export const isReservedProp = /*#__PURE__*/ makeMap(
// the leading comma is intentional so empty string "" is also included
',key,ref,ref_for,ref_key,' +
'onVnodeBeforeMount,onVnodeMounted,' +
'onVnodeBeforeUpdate,onVnodeUpdated,' +
'onVnodeBeforeUnmount,onVnodeUnmounted'
)
24、isBuiltInDirective: 是否是内置指令
export const isBuiltInDirective = /*#__PURE__*/ makeMap(
'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo'
)
25、cacheStringFunction: 缓存字符串的函数
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
}
26、camelize: "-"连字符转小驼峰
const camelizeRE = /-(\w)/g
/**
* @private
*/
export const camelize = cacheStringFunction((str: string): string => {
return str.replace(camelizeRE, (_, c) => (c ? c.toUpperCase() : ''))
})
27、hyphenate: 大写字母转"-"连字符
const hyphenateRE = /\B([A-Z])/g
/**
* @private
*/
export const hyphenate = cacheStringFunction((str: string) =>
str.replace(hyphenateRE, '-$1').toLowerCase()
)
28、capitalize: 首字母转大写
/**
* @private
*/
export const capitalize = cacheStringFunction(
(str: string) => str.charAt(0).toUpperCase() + str.slice(1)
)
29、toHandlerKey: 转成带on的写法
/**
* @private
*/
export const toHandlerKey = cacheStringFunction((str: string) =>
str ? `on${capitalize(str)}` : ``
)
例: toHandlerKey('click') --> onClick
30、hasChanged:判断值是否有变化
// compare whether a value has changed, accounting for NaN.
export const hasChanged = (value: any, oldValue: any): boolean =>
!Object.is(value, oldValue)
31、invokeArrayFns: 遍历执行数组里的函数
export const invokeArrayFns = (fns: Function[], arg?: any) => {
for (let i = 0; i < fns.length; i++) {
fns[i](arg)
}
}
32、def: 定义一个不可枚举的对象
export const def = (obj: object, key: string | symbol, value: any) => {
Object.defineProperty(obj, key, {
configurable: true, // 可以删除
enumerable: false, // 不可以被枚举
value
})
}
Object.defineProperty的描述符包含以下几种:
configurable: 是否可删除,默认为:falseenumerable: 是否可以被枚举(遍历操作的时候),默认为:falsevalue:该属性对应的值,默认为:undefinedwritable:是否可修改,默认为:falseget:获取属性值时所调用的函数,默认为:undefinedset:设置属性值时所调用的函数,默认为:undefined
33、toNumber: 转数字
export const toNumber = (val: any): any => {
const n = parseFloat(val)
return isNaN(n) ? val : n
}
34、getGlobalThis: 获取全局对象
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
: {})
)
}
globalThis在 nodejs 环境就是globalglobalThis在 浏览器 环境就是window- 还可能在别的环境(比如某个app内,小程序内等)
vue3可以自定义渲染器接入到不同的环境中哦