1、cached
/**
* Create a cached version of a pure function.
*/
function cached (fn) {
var cache = Object.create(null);//es6语法,创建一个空对象
return (function cachedFn (str) {
var hit = cache[str];
console.log(cache) //加的代码,打印cache,观察变化
return hit || (cache[str] = fn(str)) //如果hit有值就返回hit,没有返回fn的返回值,并且将该值赋给cache对象
})
}
然后咱们写个小例子来调用
var fn=(val)=>{
return val;
}//一个返回自身输入的函数
var rsFn=cached(fn);//这变其实是返回的一个function
rsFn(1) //打印{}
rsFn(2) //打印{1:1}
rsFn(1) //当你再次调用时,{1:1}其实已经被缓存下来了,用了闭包,所以cache对象会一直存在于内存中
//这时候取1,就会直接去取出cache对象中的值,而不会再次调用方法。
从而就实现了一个函数式的方法,只要你想要将你缓存,输入输出的函数,作为参数传入,就会为你实现一个缓存数据的功能。
2、polyfillBind
每次加元素计算数组长度(这里以数组数据类型为例)
/* istanbul ignore next */
function polyfillBind (fn, ctx) {
function boundFn (a) {
var l = arguments.length;
console.log(ctx) 打印ctx,进行观察
return l
? l > 1
? fn.apply(ctx, arguments)//多个对参数数组apply,返回总长度
: fn.call(ctx, a)//一个参数就直接call,返回总长度
: fn.call(ctx) //没有新参数,就返回原长度
}
boundFn._length = fn.length;
return boundFn
}
var rsFn=polyfillBind(Array.prototype.push,[])//传入一个初始数组对象
rsFn(1) //[] // 1 因为数组打印在前,所以值都会滞后一步
rsFn(2) //[1] //2
rsFn([1,2]) //添一个数组//[1, 2]
rsFn() // [1, 2, Array(2)]
rsFn() // [1, 2, Array(2)]
3、looseEqual
对对象的浅相等进行判断
/**
* Check if two values are loosely equal - that is,
* if they are plain objects, do they have the same shape?
*/
function looseEqual (a, b) {
if (a === b) { return true }
var isObjectA = isObject(a);
var isObjectB = isObject(b);
if (isObjectA && isObjectB) {
try {
var isArrayA = Array.isArray(a);
var isArrayB = Array.isArray(b);
if (isArrayA && isArrayB) {
return a.length === b.length && a.every(function (e, i) {
return looseEqual(e, b[i])
})
} else if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime()
} else if (!isArrayA && !isArrayB) {
var keysA = Object.keys(a);
var keysB = Object.keys(b);
return keysA.length === keysB.length && keysA.every(function (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
}
}
4、解读Dep,Observer和Watcher
Vue实例化一个对象的具体过程如下:
- 新创建一个实例后,Vue调用compile将el转换成vnode。
- 调用initState, 创建props, data的钩子以及其对象成员的Observer(添加getter和setter)。
- 执行mount挂载操作,在挂载时建立一个直接对应render的Watcher,并且编译模板生成render函数,执行vm._update来更新DOM。
- 每当有数据改变,都将通知相应的Watcher执行回调函数,更新视图。当给这个对象的某个属性赋值时,就会触发set方法。set函数调用,触发Dep的notify()向对应的Watcher通知变化。Watcher调用update方法。
在这个过程中:
- Observer是用来给数据添加Dep依赖。
- Dep是data每个对象包括子对象都拥有一个该对象, 当所绑定的数据有变更时, 通过dep.notify()通知Watcher。
- Compile是HTML指令解析器,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数。
- Watcher是连接Observer和Compile的桥梁,Compile解析指令时会创建一个对应的Watcher并绑定update方法 , 添加到Dep对象上。