1.代理模式。在某些情況下,出于種種考慮/限制,一個對象不能直接訪問另一個對象,需要一個第三者(代理)牽線搭橋從而間接達到訪問目的,這樣的模式就是代理模式。
2.業(yè)務開發(fā)中最常見的四種代理類型
(1)事件代理
用代理模式實現(xiàn)多個子元素的事件監(jiān)聽
// 獲取父元素
const father = document.getElementById('father')
// 給父元素安裝一次監(jiān)聽函數(shù)
father.addEventListener('click', function(e) {
// 識別是否是目標子元素
if(e.target.tagName === 'A') {
// 以下是監(jiān)聽函數(shù)的函數(shù)體
e.preventDefault()
alert(`我是${e.target.innerText}`)
}
} )
在這種做法下,我們的點擊操作并不會直接觸及目標子元素,而是由父元素對事件進行處理和分發(fā)、間接地將其作用于子元素,因此這種操作從模式上劃分屬于代理模式。
(2)虛擬代理
圖片預加載
class PreLoadImage {
constructor(imgNode) {
// 獲取真實的DOM節(jié)點
this.imgNode = imgNode
}
// 操作img節(jié)點的src屬性
setSrc(imgUrl) {
this.imgNode.src = imgUrl
}
}
class ProxyImage {
// 占位圖的url地址
static LOADING_URL = 'xxxxxx'
constructor(targetImage) {
// 目標Image,即PreLoadImage實例
this.targetImage = targetImage
}
// 該方法主要操作虛擬Image,完成加載
setSrc(targetUrl) {
// 真實img節(jié)點初始化時展示的是一個占位圖
this.targetImage.setSrc(ProxyImage.LOADING_URL)
// 創(chuàng)建一個幫我們加載圖片的虛擬Image實例
const virtualImage = new Image()
// 監(jiān)聽目標圖片加載的情況,完成時再將DOM上的真實img節(jié)點的src屬性設置為目標圖片的url
virtualImage.onload = () => {
this.targetImage.setSrc(targetUrl)
}
// 設置src屬性,虛擬Image實例開始加載圖片
virtualImage.src = targetUrl
}
}
PreLoadImage 專心去做 DOM 層面的事情(真實 DOM 節(jié)點的獲取、img 節(jié)點的鏈接設置).
ProxyImage 幫我們調(diào)度了預加載相關的工作,我們可以通過 ProxyImage 這個代理,實現(xiàn)對真實 img 節(jié)點的間接訪問,并得到我們想要的效果。
在這個實例中,virtualImage 這個對象是一個“幕后英雄”,它始終存在于 JavaScript 世界中、代替真實 DOM 發(fā)起了圖片加載請求、完成了圖片加載工作,卻從未在渲染層面拋頭露面。因此這種模式被稱為“虛擬代理”模式。
(3)緩存代理
應用于一些計算量較大的場景里。在這種場景下,我們需要“用空間換時間”——當我們需要用到某個已經(jīng)計算過的值的時候,不想再耗時進行二次計算,而是希望能從內(nèi)存里去取出現(xiàn)成的計算結果。這種場景下,就需要一個代理來幫我們在進行計算的同時,進行計算結果的緩存了。
(4)保護代理
Proxy,它本身就是為攔截而生的,所以我們目前實現(xiàn)保護代理時,考慮的首要方案就是 ES6 中的 Proxy。
Proxy可以理解成,在目標對象之前架設一層攔截,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
let obj = new Proxy({},{
get:function(target,propKey,receiver){
console.log(`getting ${propKey}!`)
return Reflect.get(target, propKey, receiver)
},
set: function(target, propKey,value,receiver){
console.log(`setting ${propKey}!`)
return Reflect.set(target, propKey, value, receiver)
}
})
obj.count = 1
// setting count!
console.log(++obj.count)
// getting count!
// setting count!
// 2
3.代理模式的目的是十分多樣化的,既可以是為了加強控制、拓展功能、提高性能,也可以僅僅是為了優(yōu)化我們的代碼結構、實現(xiàn)功能的解耦。無論是出于什么目的,這種模式的套路就只有一個—— A 不能直接訪問 B,A 需要借助一個幫手來訪問 B,這個幫手就是代理器。需要代理器出面解決的問題,就是代理模式發(fā)光發(fā)熱的應用場景。