JavaScript 處理樹結構數據
場景
即便在前端,也有很多時候需要操作 樹結構 的情況,最典型的場景莫過于 無限級分類。之前吾輩曾經遇到過這種場景,但當時沒有多想直接手撕 JavaScript 列表轉樹了,并沒有想到進行封裝。后來遇到的場景多了,想到如何封裝樹結構操作,但考慮到不同場景的樹節點結構的不同,就沒有繼續進行下去了。
直到吾輩開始經常運用了 ES6 Proxy 之后,吾輩想到了新的解決方案!
思考
問: 之前為什么停止封裝樹結構操作了?
答: 因為不同的樹結構節點可能有不同的結構,例如某個項目的樹節點父節點 id 字段是 parent,而另一個項目則是 parentId
問: Proxy 如何解決這個問題呢?
答: Proxy 可以攔截對象的操作,當訪問對象不存在的字段時,Proxy 能將之代理到已經存在的字段上
問: 這點意味著什么?
答: 它意味著 Proxy 能夠抹平不同的樹節點結構之間的差異!
問: 我還是不太明白 Proxy 怎么用,能舉個具體的例子么?
答: 當然可以,我現在就讓你看看 Proxy 的能力
下面思考一下如何在同一個函數中處理這兩種樹節點結構
/** * 系統菜單 */class SysMenu { /** * 構造函數 * @param {Number} id 菜單 id * @param {String} name 顯示的名稱 * @param {Number} parent 父級菜單 id */ constructor(id, name, parent) { this.id = id this.name = name this.parent = parent }}/** * 系統權限 */class SysPermission { /** * 構造函數 * @param {String} uid 系統唯一 uuid * @param {String} label 顯示的菜單名 * @param {String} parentId 父級權限 uid */ constructor(uid, label, parentId) { this.uid = uid this.label = label this.parentId = parentId }}下面讓我們使用 Proxy 來抹平訪問它們之間的差異
const sysMenuMap = new Map().set('parentId', 'parent')const sysMenu = new Proxy(new SysMenu(1, 'rx', 0), { get(_, k) { if (sysMenuMap.has(k)) { return Reflect.get(_, sysMenuMap.get(k)) } return Reflect.get(_, k) },})console.log(sysMenu.id, sysMenu.name, sysMenu.parentId) // 1 'rx' 0const sysPermissionMap = new Map().set('id', 'uid').set('name', 'label')const sysPermission = new Proxy(new SysPermission(1, 'rx', 0), { get(_, k) { if (sysPermissionMap.has(k)) { return Reflect.get(_, sysPermissionMap.get(k)) } return Reflect.get(_, k) },})console.log(sysPermission.id, sysPermission.name, sysPermission.parentId) // 1 'rx' 0定義橋接函數
現在,差異確實抹平了,我們可以通過訪問相同的屬性來獲取到不同結構對象的值!然而,每個對象都寫一次代理終究有點麻煩,所以我們實現一個通用函數用以包裝。
/** * 橋接對象不存在的字段 * @param {Object} map 代理的字段映射 Map * @returns {Function} 轉換一個對象為代理對象 */export function bridge(map) { /** * 為對象添加代理的函數 * @param {Object} obj 任何對象 * @returns {Proxy} 代理后的對象 */ return function(obj) { return new Proxy(obj, { get(target, k) { if (Reflect.has(map, k)) { return Reflect.get(target, Reflect.get(map, k)) } return Reflect.get(target, k) }, set(target, k, v) { if (Reflect.has(map, k)) { Reflect.set(target, Reflect.get(map, k), v) return true } Reflect.set(target, k, v) return true }, }) }}
新聞熱點
疑難解答
圖片精選