前言
最近开始学习vue2源码,于是开设了源码专栏,以此记录自己的学习历程。
本文主要介绍了
- vue2的基本工具函数的功能
- 源码中的思想如何使用到自己的项目中
- 总结
基本工具函数的功能
以下工具函数的源码shared 模块,对应的文件路径是:vue/vue/src/shared。
可以直接访问源码:github.com/vuejs/vue/b…
1. isUndef
/** 函数功能:判断是否是未定义 */
export function isUndef (v: any): boolean %checks {
return v === undefined || v === null
}
2. isDef
/** 函数功能: 判断是否已经定义 */
export function isDef (v: any): boolean %checks {
return v !== undefined && v !== null
}
3. isTrue
export function isTrue (v: any): boolean %checks {
return v === true
}
4. isFalse
/** 函数功能:判断是否为false */
export function isFalse (v: any): boolean %checks {
return v === false
}
5. isPrimitive
/** 函数功能:判断值是否是原始值:string, number或者boolean,后续还可以加入symbol对象 */
export function isPrimitive (value: any): boolean %checks {
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
)
}
6. isObject
/** 函数功能:判断值是否是对象 */
export function isObject (obj: mixed): boolean %checks {
return obj !== null && typeof obj === 'object'
}
7. toRawType
const _toString = Object.prototype.toString // 返回一个表示该对象的字符串[object Object]
/** 函数功能:返回一个表示该对象的字符串的后几位。如:toRawType(''),就返回'string' */
export function toRawType (value: any): string {
return _toString.call(value).slice(8, -1)
}
8. isPlainObject
/** 函数功能:返回是否是纯对象,纯对象就是值为{}之类,即当前变量是对象,当前对象的原型是其原型链中的顶级原型 */
export function isPlainObject (obj: any): boolean {
return _toString.call(obj) === '[object Object]'
}
9. isRegExp
/** 函数功能:返回是否是正则表达式 */
export function isRegExp (v: any): boolean {
return _toString.call(v) === '[object RegExp]'
}
10. isValidArrayIndex
/** 函数功能:返回是否是可用的数组索引值 */
export function isValidArrayIndex (val: any): boolean {
const n = parseFloat(String(val))
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
11. toString
/** 函数功能:转成字符串,对象用JSON.stringify转换 */
export function toString (val: any): string {
return val == null
? ''
: typeof val === 'object'
? JSON.stringify(val, null, 2)
: String(val)
}
12. toNumber
/** 函数功能:转成数字,如果转换失败依旧返回原始字符串 */
export function toNumber (val: string): number | string {
const n = parseFloat(val)
return isNaN(n) ? val : n
}
13. makeMap
/** 函数功能:生成一个map对象
* 传入一个以逗号分隔的字符串,生成一个map(键值对),并且返回一个函数检测key值在不在这个map中
* 第二个参数是小写选项: true为不区分大小写,false为区分,参数是什么就是什么
*/
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]
}
14. isBuiltInTag
/** 函数功能:是否是内置的tag,slot或component */
export const isBuiltInTag = makeMap('slot,component', true)
15. isReservedAttribute
/** 函数功能:是否是保留属性,如key, ref, slot, slot-scope, is */
export const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is')
16. remove
/** 函数功能:删除数组中的某一项
* splice其实是一个很耗性能的方法。删除数组中的一项,其他元素都要移动位置
*/
export function remove (arr: Array<any>, item: any): Array<any> | void {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
17. hasOwn
/** 函数功能:检测是否是自己的属性 */
const hasOwnProperty = Object.prototype.hasOwnProperty
export function hasOwn (obj: Object | Array<*>, key: string): boolean {
return hasOwnProperty.call(obj, key)
}
18. cached
/** 函数功能:利用闭包,缓存数据 */
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)
}
19. camelize
/** 函数功能:将连字符转成小驼峰
* 例子: on-click ----> onClick
*/
const camelizeRE = /-(\w)/g
export const camelize = cached((str: string): string => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})
20. capitalize
/** 函数功能:首字母大写
*/
export const capitalize = cached((str: string): string => {
return str.charAt(0).toUpperCase() + str.slice(1)
})
21. hyphenate
/** 函数功能:小驼峰转连字符 */
const hyphenateRE = /\B([A-Z])/g
export const hyphenate = cached((str: string): string => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
})
22.bind
/** 函数功能:简单来说就是兼容了老版本浏览器不支持原生的bind函数。同时兼容写法,对参数的多少做出了判断,使用call和apply实现,据说参数多适合apply,少用call性能好 */
export function bind (fn: Function, ctx: Object): Function {
function boundFn (a) {
const l: number = arguments.length
return l
? l > 1
? fn.apply(ctx, arguments)
: fn.call(ctx, a)
: fn.call(ctx)
}
// record original fn length
boundFn._length = fn.length
return boundFn
}
23. toArray
/** 函数功能:把类数组转成真正的数组
* 支持从哪个位置开始,默认从0开始
*/
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
}
24. extend
/** 函数功能:合并两个对象 */
export function extend (to: Object, _from: ?Object): Object {
for (const key in _from) {
to[key] = _from[key]
}
return to
}
25. 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
}
26. noop
/** 函数功能:空函数 */
export function noop (a?: any, b?: any, c?: any) {}
27. no
/** 函数功能:一直返回false */
export const no = (a?: any, b?: any, c?: any) => false
28. identity
/** 函数功能:返回参数本身 */
export const identity = (_: any) => _
29. genStaticKeys
/** 函数功能:生成静态属性 */
export function (modules: Array<ModuleOptions>): string {
return modules.reduce((keys, m) => {
return keys.concat(m.staticKeys || [])
}, []).join(',')
}
30. 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 (!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
}
}
31. looseIndexOf
/** 函数功能:返回宽松相等的数据的索引, 原生的indexOf是严格相等的index
* 索引存在即返回对应的索引,没有的话就返回-1
*/
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
}
32. once
/** 函数功能:确保函数只能执行一次
* 利用闭包特性,存储状态
*/
export function once (fn: Function): Function {
let called = false
return function () {
if (!called) {
called = true
fn.apply(this, arguments)
}
}
}
源码中的思想如何使用到自己的项目中
通过以上代码我们可以看出,工具函数主要用到了以下几个核心
- 类型判断:包括基本数据类型和引用的数据类型
- 类型转换:数组转对象,对象转数组等
- 原型:包括实例对象,原型对象之间的关系等
- 正则表达式
- 判读是否相等
- 闭包
以上这些思想其实在我们的项目中都会运用到,我们可以通过看源码了解它的实现方式。这里就不展开说了,感兴趣的话可以查看我的前端学习路线,里面会针对每个模块进行详细的介绍
总结
讲实话,这些工具函数的源码真的不难,基本上用到的js核心都是我们平常在项目中接触很多的知识,只是我们在平时项目中大多数人都是把别人写的东西拿过来用,没有系统地去整理过,对于一些稍微复杂的场景也不太清楚,总之在学习的过程中,一定要多加练习,学会总结,久而久之,就变成自己的东西了,用起来就会很得心应手了。