1.说明
本文参加了由公众号@若川视野 发起的每周源码共读活动,点击了解详情一起参与。
2.前言
本文是针对【若川视野 x 源码共读】第24期 | vue2工具函数的阅读总结笔记,该文并不会全部阐述文章中工具函数的使用,只会针对个别本人觉得复杂或者理解不透,亦或是比较耐用的工具函数,进行些许笔墨的分析。如有描述不对、不足的地方,还请及时告知,不吝感谢!
3.工具函数
emptyObject
- 创建一个空对象,一层被冻结,无法修改
- Object.freeze - MDN
var emptyObject = Object.freeze({})
复制代码
isUndef
- 是否未定义
- undefined 和 null 区别
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 函数。同时兼容写法,对参数的多少做出了判断,使用call和apply实现,据说参数多适合用 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)
}
}
}
到这里。基础知识就预习结束