在回顾Vue源码的时候,经常看到Vue中一些辅助函数,总结一些日常中会用到的,和一些js代码片段
Vue 源码中的一些辅助函数
是否定义
function isDef (v){
return v !== undefined && v !== null
}
已经定义的值判断是否是原始类型
function isPrimitive (value){
return (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
是否是 promise 类型
`function isPromise (val){ return ( isDef(val) && typeof val.then === 'function' && typeof val.catch === 'function' ) } ### 转换为数字`// 将输入值转换为数字。如果转换失败,则返回原始字符串。 function toNumber (val){ const n = parseFloat(val) return isNaN(n) ? val : n } ### 创建一个 map,返回一个函数去检测一个 key 值是否存在与这个 map`function makeMap ( str, expectsLowerCase ){ const map = Object.create(null) const list = str.split(',') for (let i = 0; i < list.length; i++) { map[list[i]] = true } return expectsLowerCase ? val => map[val.toLowerCase()] : val => map[val] } vue 源码中的使用 var isHTMLTag = makeMap( 'html,body,base,head,link,meta,style,title,' + 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' + 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' + 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' + 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' + 'embed,object,param,source,canvas,script,noscript,del,ins,' + 'caption,col,colgroup,table,thead,tbody,td,th,tr,' + 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' + 'output,progress,select,textarea,' + 'details,dialog,menu,menuitem,summary,' + 'content,element,shadow,template,blockquote,iframe,tfoot' ); var isSVG = makeMap( 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' + 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' + 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view', true ); var isReservedTag = function (tag) { return isHTMLTag(tag) || isSVG(tag) }; var isBuiltInTag = makeMap('slot,component', true) function validateComponentName (name) { if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + (unicoderegexp.source) + "]\*\$")).test(name)) { warn( 'Invalid component name: "' + name + '". Component names ' + 'should conform to valid custom element name in html5 specification.' ); } if (isBuiltInTag(name) || config.isReservedTag(name)) { warn( 'Do not use built-in or reserved HTML elements as component ' + 'id: ' + name ); } } ### 从数组中移除一项`function remove (arr, item){ if (arr.length) { const index = arr.indexOf(item) if (index > -1) { return arr.splice(index, 1) } } } vue 源码中的使用 Dep.prototype.removeSub = function removeSub (sub) { remove(this.subs, sub); }; ### 转换一个类数组为真实数组`function toArray (list, start){ start = start || 0 let i = list.length - start const ret = new Array(i) while (i--) { ret[i] = list[i + start] } return ret } vue 源码中的使用 Vue.prototype.$emit = function (event) { const vm = this; ... let cbs = vm._events[event]; if (cbs) { cbs = cbs.length > 1 ? toArray(cbs) : cbs; const args = toArray(arguments, 1); ... } return vm }; ### 将属性混合到目标对象中`function extend (to, _from): Object { for (const key in _from) { to[key] = _from[key] } return to } ### 将对象数组合并为单个对象`function toObject (arr) { var res = {}; for (var i = 0; i < arr.length; i++) { if (arr[i]) { extend(res, arr[i]); } } return res } vue 源码中的使用 function updateStyle (oldVnode: VNodeWithData, vnode: VNodeWithData) { ... if (Array.isArray(style)) { style = vnode.data.style = toObject(style) } } ### 确保一个函数仅被调用一次`function once (fn) { var called = false; return function () { if (!called) { called = true; fn.apply(this, arguments); } } } vue 源码中的使用 const reject = once(reason => { warn( `Failed to resolve async component: ${String(factory)}` + (reason ? `\nReason: ${reason}` : '') ); if (isDef(factory.errorComp)) { factory.error = true; forceRender(true); } });`定义一个属性 function def (obj, key, val, enumerable) { Object.defineProperty(obj, key, { value: val, enumerable: !!enumerable, writable: true, configurable: true }); } 通过使用__proto__的原型链拦截来增强目标对象或数组 function protoAugment (target, src) { target.__proto__ = src; } 通过定义来增强目标对象或数组 隐藏的属性。 function copyAugment (target, src, keys) { for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; def(target, key, src[key]); } } vue 源码中的使用 export class Observer { value: any; dep: Dep; vmCount: number; constructor (value: any) { this.value = value this.dep = new Dep() this.vmCount = 0 def(value, '__ob__', this) if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } this.observeArray(value) } else { this.walk(value) } } ... } ## 日常使用的js代码片段 ### 取数组相同项(不同项同理取不包含) 选择一个数组创建 Set,然后使用 Array.filter() 过滤 Set 中包含的另一个数组值`const intersection = (ary1, ary2) => { const s = new Set(ary2); return ary1.filter(item => s.has(item)) }; //如果确定数组中不会有重复部分 const intersection = (ary1, ary2) => ary1.filter(v => ary2.includes(v)); // intersection([32,43,543], [32,12,55,43]) -> [32,43] ### 统计数组中出现指定值的次数 用 reduce 查询数组,符合条件时+1`const countOccurrences = (ary, value) => ary.reduce((res, val) => val === value ? ++res : res + 0, 0); //countOccurrences([2,4,11,'a','a','b',32], 'a') -> 2 ### 扁平化数组(扁平所有) 递归数组,reduce 展开数组并合并`const deepFlatten = arr => arr.reduce((a, v) => a.concat(Array.isArray(v) ? deepFlatten(v) : v), []); // deepFlatten(['a',['b'],[['c'],'d']]) -> ['a','b','c','d'] //如果所要展开的只有一层 可以直接使用 es6 的 Array.flat(),且只能展开一层 ['a',['b'],['c'],'d'].flat() -> ['a','b','c','d']'a',['b'],[['c'],'d']].flat() -> ["a", "b", ['c'], "d"] ### 扁平化指定深度的数组 在上一个的基础上改造一下,增加一个深度参数`const flattenDepth = (ary, depth = 1) => depth != 1 ? ary.reduce((a, v) => a.concat(Array.isArray(v) ? flattenDepth(v, depth - 1) : v), []) : ary.flat(); // flattenDepth(['a','b',['c'],[['d','e',['f'] ]]], 2) -> ["a", "b", "c", "d", "e", ['f']] ### 滚动到顶部`const scrollToTop = _ => { const c = document.documentElement.scrollTop || document.body.scrollTop; //获取到顶端的距离 if (c > 0) { window.requestAnimationFrame(scrollToTop);//滚动动画效果 window.scrollTo(0, c - c / 8); } }; ### 取两个日期之间相差的天数`const getDaysDiffBetweenDates = (beginDate, endDate) => ( endDate - beginDate) / (1000 _ 3600 _ 24); // getDaysDiffBetweenDates(new Date("2020-09-22"), new Date("2020-10-01")) -> 9