本文不准备解析vue源码的运行原理,仅单纯探寻vue中工具函数中那些值得学习的骚操作
终极目标:从工具函数中扩展知识点
// 通过判断 `window` 对象是否存在即可
export const inBrowser = typeof window !== 'undefined'
// 一个对象的 __proto__ 属性指向了其构造函数的原型
// 从一个空的对象字面量开始沿着原型链逐级检查。
export const hasProto = '__proto__' in {}
// toLowerCase目的是 为了后续的各种环境检测
export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
export const isIE = UA && /msie|trident/.test(UA)
解析:使用正则去匹配 UA 中是否包含'msie'或者'trident'这两个字符串即可判断是否为 IE 浏览器
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
export const isEdge = UA && UA.indexOf('edge/') > 0
export const isChrome = UA && /chrome\/\d+/.test(UA) && !isEdge
// charCodeAt() 方法可返回指定位置的字符的 Unicode 编码
export function isReserved (str: string): boolean {
const c = (str + '').charCodeAt(0)
return c === 0x24 || c === 0x5F
}
解析: 获得该字符串第一个字符的unicode,然后与 0x24 和 0x5F 作比较。
若作为一个想进阶中高级的前端,charCodeAt方法的各种妙用还是需要知道的(面试算法题各种考)。
function fearNotLetter(str) {
//将字符串转为ASCII码,并存入数组
let arr=[];
for(let i=0; i<str.length; i++){
arr.push(str.charCodeAt(i));
}
for(let j=1; j<arr.length; j++){
let num=arr[j]-arr[j-1];
//判断后一项减前一项是否为1,若不为1,则缺失该字符的前一项
if(num!=1){
//将缺失字符ASCII转为字符并返回
return String.fromCharCode(arr[j]-1);
}
}
return undefined;
}
fearNotLetter("abce") // "d"
const camelizeRE = /-(\w)/g
export const camelize = cached((str: string): string => {
return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '')
})
解析: 定义正则表达式:/-(\w)/g,用来全局匹配字符串中 中横线及连字符后的一个字符。若捕获到,则将字符以toUpperCase大写替换,否则以''替换。 如:camelize('aa-bb') // aaBb
export function toString (val: any): string {
return val == null
? ''
: typeof val === 'object'
? JSON.stringify(val, null, 2)
: String(val)
}
解析: 在Vue中充斥着很多这类增强型的封装,大大减少了我们代码的复杂性。但这里,我们要学习的是这种多重三元运算符的用法
export function toString (val: any): string {
return val == null
? ''
: typeof val === 'object'
? JSON.stringify(val, null, 2)
: String(val)
}
解析:
export function toString (val: any): string {
return 当变量值为 null 时
? 返回空字符串
: 否则,判断当变量类型为 object时
? 返回 JSON.stringify(val, null, 2)
: 否则 String(val)
}
类似的操作在vue源码里很多。比如mergeHook
function mergeHook (
parentVal: ?Array<Function>,
childVal: ?Function | ?Array<Function>
): ?Array<Function> {
return childVal
? parentVal
? parentVal.concat(childVal)
: Array.isArray(childVal)
? childVal
: [childVal]
: parentVal
}
这里我们不关心mergeHook在源码中是做什么的(其实是判断父子组件有无对应名字的生命周期钩子函数,然后将其通过 concat合并
// 忽略cached
export const capitalize = cached((str: string): string => {
return str.charAt(0).toUpperCase() + str.slice(1)
})
解析: str.charAt(0)获取str的第一项,利用toUpperCase()转换为大写字母,str.slice(1) 截取除第一项的str部分。
const hyphenateRE = /\B([A-Z])/g
export const hyphenate = cached((str: string): string => {
return str.replace(hyphenateRE, '-$1').toLowerCase()
})
解析: 与camelize相反。实现方式同样是使用正则,/\B([A-Z])/g用来全局匹配字符串中的大写字母, 然后替换掉。
export function isPrimitive (value: any): boolean %checks {
return (
typeof value === 'string' ||
typeof value === 'number' ||
// $flow-disable-line
typeof value === 'symbol' ||
typeof value === 'boolean'
)
}
解析: 这个很简单,但我们经常忽略掉symbol这个类型(虽然完全没用过)。
// 使用 Object.prototype.toString 与 '[object RegExp]' 做全等对比。
export function isRegExp (v: any): boolean {
return _toString.call(v) === '[object RegExp]'
}
这也是最准确的类型判断方法,在Vue中其它类型也是一样的判断
export function isValidArrayIndex (val: any): boolean {
const n = parseFloat(String(val))
// n >= 0 && Math.floor(n) === n 保证了索引是一个大于等于 0 的整数
return n >= 0 && Math.floor(n) === n && isFinite(val)
}
isFinite方法检测它参数的数值。如果参数是NaN,正无穷大或者负无穷大,会返回false,其他返回true。扩展:语法:isFinite()
export function isObject (obj: mixed): boolean %checks {
return obj !== null && typeof obj === 'object'
}
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]
}
定义一个对象map
将 str 分隔成数组并保存到 list 变量中
遍历list,并以list中的元素作为 map 的 key,将其设置为 true
返回一个函数,并且如果expectsLowerCase为true的话,小写map[key]:
我们用一个例子来说明下:
let isMyName = makeMap('前端劝退师,帅比',true);
//设定一个检测是否为我的名字的方法,第二个参数不区分大小写
isMyName('前端劝退师') // true
isMyName('帅比') // true
isMyName('丑逼') // false
Vue中类似的判断非常多,也很实用。
这三个函数是通过 makeMap 生成的,用来检测一个属性(标签)是否为保留属性(标签)
export const 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'
)
export const 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
)
// web平台的保留属性有 style 和 class
export const isReservedAttr = makeMap('style,class')
export function once (fn: Function): Function {
let called = false
return function () {
if (!called) {
called = true
fn.apply(this, arguments)
}
}
}
解析: 以called作为回调标识符。调用此函数时,called标示符改变,下次调用就无效了。也是典型的闭包调用。
/**
* Create a cached version of a pure function.
*/
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)
}
解析: 这里的注释已把作用解释了。
const cache = Object.create(null)创建纯函数是为了防止变化(纯函数的特性:输入不变则输出不变)。
在Vue中,需要转译很多相同的字符串,若每次都重新执行转译,会造成很多不必要的开销。 cache这个函数可以读取缓存,如果缓存中没有就存放到缓存中,最后再读。
export function looseEqual (a: any, b: any): boolean {
// 当 a === b 时,返回true
if (a === b) return true
// 否则进入isObject判断
const isObjectA = isObject(a)
const isObjectB = isObject(b)
// 判断是否都为Object类型
if (isObjectA && isObjectB) {
try {
// 调用 Array.isArray() 方法,再次进行判断
// isObject 不能区分是真数组还是对象(typeof)
const isArrayA = Array.isArray(a)
const isArrayB = Array.isArray(b)
// 判断是否都为数组
if (isArrayA && isArrayB) {
// 对比a、bs数组的长度
return a.length === b.length && a.every((e, i) => {
// 调用 looseEqual 进入递归
return looseEqual(e, b[i])
})
} else if (!isArrayA && !isArrayB) {
// 均不为数组,获取a、b对象的key集合
const keysA = Object.keys(a)
const keysB = Object.keys(b)
// 对比a、b对象的key集合长度
return keysA.length === keysB.length && keysA.every(key => {
//长度相等,则调用 looseEqual 进入递归
return looseEqual(a[key], b[key])
})
} else {
// 如果a、b中一个是数组,一个是对象,直接返回 false
/* istanbul ignore next */
return false
}
} catch (e) {
/* istanbul ignore next */
return false
}
} else if (!isObjectA && !isObjectB) {
return String(a) === String(b)
} else {
return false
}
}
这个函数比较长,建议配合注释食用。 总之,就是
作者:前端劝退师
链接:https://juejin.im/post/6844903782719946765
来源:掘金
在日常 Coding 中,码农们肯定少不了对数组的操作,其中很常用的一个操作就是对数组进行遍历,查看数组中的元素,然后一顿操作猛如虎。今天暂且简单地说说在 JavaScript 中 forEach。
克隆项目代码到本地(git应该都要会哈,现在源码几乎都会放github上,会git才方便,不会的可以自学一下哦,不会的也没关系,gitHub上也提供直接下载的链接);打开微信开发者工具;
随着这些模块逐渐完善, Nodejs 在服务端的使用场景也越来越丰富,如果你仅仅是因为JS 这个后缀而注意到它的话, 那么我希望你能暂停脚步,好好了解一下这门年轻的语言,相信它会给你带来惊喜
在 Vue 内部,有一段这样的代码:上面5个函数的作用是在Vue的原型上面挂载方法。initMixin 函数;可以看到在 initMixin 方法中,实现了一系列的初始化操作,包括生命周期流程以及响应式系统流程的启动
nextTick的使用:vue中dom的更像并不是实时的,当数据改变后,vue会把渲染watcher添加到异步队列,异步执行,同步代码执行完成后再统一修改dom,我们看下面的代码。
React更新的方式有三种:(1)ReactDOM.render() || hydrate(ReactDOMServer渲染)(2)setState(3)forceUpdate;接下来,我们就来看下ReactDOM.render()源码
在React中,为防止某个update因为优先级的原因一直被打断而未能执行。React会设置一个ExpirationTime,当时间到了ExpirationTime的时候,如果某个update还未执行的话,React将会强制执行该update,这就是ExpirationTime的作用。
算法对于前端工程师来说总有一层神秘色彩,这篇文章通过解读V8源码,带你探索 Array.prototype.sort 函数下的算法实现。来,先把你用过的和听说过的排序算法都列出来:
extend是jQuery中一个比较核心的代码,如果有查看jQuery的源码的话,就会发现jQuery在多处调用了extend方法。作用:对任意对象进行扩;’扩展某个实例对象
state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!