在回顧Vue源碼的時候,常常看到Vue中一些輔助函數,總結一些平常中會用到的,和一些js代碼片斷html
function isDef (v){
return v !== undefined && v !== null } 複製代碼
function isPrimitive (value){
return ( typeof value === 'string' || typeof value === 'number' || typeof value === 'symbol' || typeof value === 'boolean' ) } 複製代碼
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 } 複製代碼
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) } } ... } 複製代碼
選擇一個數組建立 Set,而後使用 Array.filter() 過濾 Set 中包含的另外一個數組值vue
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 查詢數組,符合條件時+1html5
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 展開數組併合並node
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"] 複製代碼
在上一個的基礎上改造一下,增長一個深度參數es6
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 複製代碼
本文使用 mdnice 排版canvas