写点什么

JavaScript 面试系列:JavaScript 设计模式之桥接模式和懒加载

作者:Jerry Wang
  • 2021 年 12 月 20 日
  • 本文字数:1764 字

    阅读完需:约 6 分钟

JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载


设计模式(Design Pattern)中的桥接模式,有的朋友平时工作可能很少用到。桥接模式的核心在于将抽象部分和它的实现部分分离,使它们都可以独立的变化。听起来很抽象,让我们看一个具体而简单的例子,通过这个例子一步步的完善来加深对桥接模式的理解。


很多论坛点登录按钮时,



周围背景都会暗下来,这样可以突出即将弹出的登录框,让用户把精力集中在用户名和密码的输入上去。



很多论坛对于这种背景变暗的 UI 实现,是创建了一个 HTML 原生的 div 元素,加上一些精心设计过背景颜色的 CSS 样式来完成的。


我们下面称这种 div 元素为遮罩层 div 元素,即 mask div。


下面讨论创建 mask div 的最优解。

实现版本 1

创建一个 createMask 函数,作为登录按钮的事件响应函数。每次点击按钮之后执行该函数。


var createMask = function(){
return document.body.appendChild( document. createElement('div') );
}
$(‘#logon_button').click(function(){
var mask = createMask();
mask.show();
})
复制代码


版本 1 的缺点

每次点击按钮都会创建一个 mask div。当然一般情况下登录按钮只会点击一次。但是在面试场景中,面试官可能会把这个问题的讨论引导到其他方向上。如何实现即使多次点击按钮,也只会创建一次 mask div?于是就有了版本 2。

实现版本 2

事先创建好一个 mask div,放到一个全局变量里保存。这种方式有点像单例模式(singleton)的饿汉式单例。



var mask = document.body.appendChild(document.createElement('div' ) );
$( '#logon_button').click(function(){
mask.show();
})
复制代码


版本 2 的缺点

版本 2 采用了一个全局变量保存事先创建好的 mask div。还记得那句话么?全局变量是万恶之源。


另外,假设用户永远不点登录按钮,只是以游客身份浏览网站,那么这个 mask div 就白白创建了。

实现版本 3


var mask;
var createMask = function(){
if(mask)
return mask;
else{
mask = document,body.appendChild( document.createElement('div') );
return mask;
}
}
复制代码


版本 3 的缺点

虽然使用了饱汉式单例模式,避免了 mask div 在没有点击登录按钮的情况下不必要的创建,但还是使用了全局变量来存放 mask div。要记住我们现在是在用 JavaScript,因此可以用它提供的强大的闭包特性(closure)来实现不需要全局变量的饱汉式单例模式。

实现版本 4

var createMask = function() {
var mask;
return function() {
return mask || ( mask = document.body.appendChild(document.createElement('div')));
}
}();
复制代码



借助 JavaScript 的闭包特性,我们在第二行创建的自由变量(Free variable)只在闭包内部可见,外部消费者感知不到这个变量,因此成为存储 mask div 的最佳选择。看起来这个版本已经很完美了?不,它仍然有可以优化的空间,即题目提到的桥接模式。

版本 4 的缺点

从单一职责原理(Single Responsibility)来衡量版本 4,createMask 函数里实际包含了两种不同类型的逻辑:


1. 创建 mask div


2. 使该 mask div “单例化”


我们下面使用桥接模式将这两种逻辑分开,来实现最终版本。

使用桥接模式的实现版本 5

这个实现包含了三个 JavaScript 函数。首先看 singleton 函数。


函数 singleton 的输入参数是另一个 JavaScript 函数(我称其为原始函数),输出是一个包装后的函数,其内部使用闭包,将原始函数第一次执行的结果保存在闭包内,当包装后的函数第二次执行时,直接返回闭包内保存的第一次执行结果。我们可以把 singleton 函数当成一个构造器,传入任意一个具有返回值的 JavaScript 函数,负责生产出具有“单例化”特性的新函数。


var singleton = function(fn){
var result;
return function() {
return result || ( result = fn.apply(this,arguments));
}
}
var origin = function(){
return document.body.appendChild(document.createElement('div'));
};
var createMask = singleton(origin);
复制代码


然后我们调用这个 singleton 函数,把我们原始的创建 mask div 的函数 origin 作为参数传进去,得到加工后的新函数 createMask。


这个例子体现了桥接模式的作用。我们通过 singleton 这个单例化构造器函数,成功将业务逻辑(创建 mask div)和单例化这个纯技术需求分离开,这样也满足了单一职责(single responsibility)的设计理念。

要获取更多 Jerry 的原创技术文章,请关注公众号"汪子熙".

发布于: 1 小时前阅读数: 5
用户头像

Jerry Wang

关注

个人微信公众号:汪子熙 2017.12.03 加入

SAP成都研究院开发专家,SAP社区导师,SAP中国技术大使。

评论

发布
暂无评论
JavaScript面试系列:JavaScript设计模式之桥接模式和懒加载