【JS设计模式】未来预言家——委托模式

238 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情

委托模式(Entrust): 多个对象接收并处理同一请求,他们将请求委托给另一个对象统一处理请求。

比如说,有下面这一段代码

<ul id="myUl">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

我希望点击 <li> 标签的时候能改变其背景色为灰色,则可以通过如下代码实现

const ul = document.getElementById('myUl')
const li = document.getElementsByTagName('li')
const li_length = li.length - 1
for(;i >= 0; i--){
    li[i].onclick = function () {
        this.style.backgroundColor = 'grey'
    }
}

上面的代码,通过for循环给所有的原始对象绑定点击事件,很简单的实现了我们的需求。但是这样做有一个问题: 无形中我们新增了很多个事件,事件一多,内存消耗就变大了,用户的体验就受到了影响。

所以我们可以换一种方式来实现,这种方式就是 代理模式。

委托父元素

一个完整的事件流,是从 事件捕获开始的, 一直到触发该事件,再到事件冒泡,一共经历3个阶段。 所以, 我们可以把子元素的事件委托给更高层次的父元素去绑定执行。

ul.onclick = function(e){
    var e = e || window.event
    const tar = e.target || e.srcElement
    if(tar.nodeName.toLowerCase() === 'li'){
        this.style.backgroundColor = 'grey'
    }
}

上面的代码中,我们监听 <ul> 标签的点击事件,然后判断目标元素的名称是否为我们要找到元素,是则执行对应的逻辑。 除了判断元素的名称,我们也可以通过判断元素的id,class以及自定义属性来进行对应的逻辑执行判断。

事件委托除了可以优化页面中的事件数量,还有一个功能,那就是 预言未来。

预言未来

什么是预言未来呢?就是有时候我们会动态的往页面上添加元素,我们想给这些新添加的元素绑定事件是很困难的,但是我们思路转变一下 将事件绑定给它的父元素,因为不过是未来还是现在,它的父元素必将是存在的,这样我们就可以为未来添加的元素执行某些逻辑了。

const article = document.getElementById('article')
article.onclick = function (e) {
    var e = e || window.event
    const tar = e.target || e.srcElement
    if(tar.nodeName.toLowerCase() === 'p'){
        tar.innerHTML = '新内容'
    }
}
const p = document.createElement('p')
p.innerHTML = '新的一段内容'
article.appendChild(p)

内存泄露

委托模式还可以用来处理一些内存泄露的问题。老版本的IE浏览器的引用计数式垃圾回收机制,使得那些对DOM元素的引用没有显性清除的数据会遗留在内存中,除非关闭浏览器,否则无法清除。 这个时候就可以使用委托模式来解决这个问题了。

数据分发

假设我们页面中的header、aside、banner模块都需要向后端请求数据,平时我们是这样做的

$.get('./deap.php?m=header',function(res){
    // 处理header模块逻辑
})
$.get('./deap.php?m=banner',function(res){})
$.get('./deap.php?m=aside',function(res){})

这样做,就会向服务器发送3次请求,当模块多起来的时候,就会造成资源的浪费,还会造成漫长的等待。这时,我们可以像下面这样,将 请求打包,委托给另一个对象发送,得到响应后再通过委托对象拆包数据分发给各个模块。

const Deal = {
    header:function(data){ /* 处理header的逻辑 */ },
    banner:function(data){ /* 处理banner的逻辑 */ },
    aside:function(data){ /* 处理aside的逻辑 */ },
}

$.get('./deap.php',function(res){
    // 数据拆包分发
    for(const key in res){
        Deal[key] && Deal[key](res[key])
    }
})

这样,本来要请求5次的,现在只需要请求一次。节省了流量和多次请求的时间开销。

总结

委托者模式 解决了请求与委托者之间的耦合。