代理模式
代理模式是对一个对象提供一个代用品或占位符,以便控制对它的访问。
代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。
客户 -> 代理 -> 对象
代理分为保护代理和虚拟代理
保护代理会在代理拒绝或处理掉一些请求。用于控制不同权限的对象对目标对象的访问,但在 JavaScript 并不容易实现保护代理,因为我们没法判断谁访问了某个对象。
虚拟代理把一些开销很大的对象延迟到真正需要它的时候才去创建。
在传统面向对象语言中,需要保证代理对象和本体接口一致,以保证用户可以放心请求代理,以及在任何使用本体的地方都可以使用代理。
const myImage = (function() {
const imgNode = document.createElement('img')
document.body.appendChild(imgNode)
return function(src) {
imgNode.src = src
}
})()
const proxyImage = (function() {
const img = new Image
img.onload = function() {
myImage(this.src)
}
return function(src) {
myImage('https://loading.jpg')
img.src = src
}
})()
proxyImage('xxx.jpg')
在这个例子中,本体和代理对象都为函数,必然能被执行,所以可以认为他们具有一致的接口。
在 JavaScript 中,说到代理,就不得不提到 Proxy 对象。Proxy 对象用于创建一个对象的代理,从而实现基本的拦截和自定义。(如属性查找、枚举、赋值、函数调用等)
我们可以用 proxy 把上面的例子重写一下。
function myImage() {
const imgNode = document.createElement('img')
const proxyImg = new Proxy(imgNode, {
set(obj, key, value) {
if(key === 'src') {
const img = new Image
img.src = value
obj[key] = 'loading.jpg'
console.log('start', obj);
img.onload = function() {
obj[key] = value
console.log('onload', obj);
}
}
}
})
return proxyImg
}
const proxyImg = myImage()
proxyImg.src = 'xxx.png'
可以看出,在 JavaScript 中,代理模式也是语言天然支持的,不需要像面向对象的语言一样定义代理类,因为 JavaScript 已经帮我们定义好了 Proxy。