Vue 源码中的一些辅助函数

在回顾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 





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值