为了改变JS中原生对象的默认行为
示例场景
假设你在代码中编写了一个通用的A方法,其他同事在他们编写的B、C、D方法中都调用了这个方法。
现在因为业务升级,你希望在A方法中添加一些新的扩展逻辑
如果不允许修改A方法的现有代码,那么你要如何来实现呢?
我们最容易想到的方法就是新建一个A2方法,
在它的函数体中先调用一次A方法,
然后再加入新增的逻辑,
这样的确可以达到上述要求,
但你也不得不将B、C、D方法中调用的A方法逐个修改为A2方法,
所以在代码实现上最好能够对调一下A和A2的函数名,
这样对于上层的B、C、D而言,它们调用的仍然是A方法,
而A方法的实现其实已经更换成了新的函数,
新函数通过调用A2方法(即原来的A方法逻辑)
保留了之前的逻辑,然后执行自己函数体中新增加的逻辑,
这样就完成了A方法的功能扩展。
其实,JavaScript中代理模式的实现也是这样一个过程
扩展知识
代理模式也称为Proxy模式,
有时还被称为“劫持”模式,
是前端使用率较高的经典设计模式中的一种,
其目的是为其他对象提供一种代理机制,
以控制对这个对象的访问。
代理模式使用代理对象来控制具体对象的引用,
代理对象几乎可以是任何对象:
文件、资源、内存中的对象,或者是一些难以复制的东西。
例如生活中的房屋中介,可以代表卖家把房子卖给买家,
这中间卖家提出期望的价钱,买家也可以提出自己心仪的户型和预算,
房产中介可以帮忙处理各类中间环节,从而促成交易,
双方都只面对中介开展业务。
从买家的视角来看,房产中介就相当于卖家的代理,
尽管他不是卖家本身,但是在交易活动中可以代表卖家。
Vue2中为了能够让数组具备“响应式”的特点,对典型的数组变异方法
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* 插入代理方法并发送事件通知
*/
methodsToPatch.forEach(function (method) {
// 缓存原方法
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// 将变化通知给监听者
ob.dep.notify()
return result
})
})
methodsToPatch.forEach(function (method) {
//保存旧方法
const original = arrayProto[method]
//定义新方法
def(arrayMethods, method, function mutator (...args) {
//执行旧方法
const result = original.apply(this, args)
//新增逻辑,用于增加响应式特性
//...
return result;
})
})
原生的方法被保存在original上(也就是将原生方法更为original),
而原生方法名则在def方法中指向了新的函数