浅谈JavaScript应用重构

来源:互联网 发布:w3cschool python 编辑:程序博客网 时间:2024/06/14 19:19

浅谈JavaScript应用重构

前言

 最近一直在做一个多屏互动的弹幕播放器的开发工作,其中“HTML使用Canvas实现弹幕功能”和“HTML5全屏模式下隐藏默认视频控制栏”是开发过程遇到的部分问题的解决方案。最近差不多把整个网页部分的代码粗糙的完成了。完整的前端页面包括点播功能,弹幕播放功能和Ajax通信功能。在一开始编写代码的过程中只是简单的代码堆砌,抽象程度不足,代码冗余度很高。基本上这两年做的大作业都有这种情况,也一直在想有时间一定进行重构,所以就先拿这个项目开刀,有的放矢,总结一下,在重构过程中使用到的方法。

设计模式之策略模式

 在重构过程中经常遇到的是一些看起来很丑陋的判断代码,如下

if(condition1) {    handle1()}else if(condition2) {    handle2()}else if(condition3) {    handle3()}.     .     ..     .     ..     .     .

当添加一个条件的时候就需要添加一层 if-else ,不知道你们怎么想反正肯定不是最佳实践。这时候我们可以使用策略模式进行相关的改造。改造的思路是使用一个 Map 将每一个情况和处理函数进行映射,然后根据条件从该 Map 中去除函数执行,代码如下

// 情况和处理函数Mapvar handleMap = {    condition1: handle1,    condition2: handle2,    condition3: handle3    . . .    . . .}// 当情况为condition时调用handleMap["condition"]()

当我们需要添加新条件和处理函数的时候,只需要向 handleMap 中添加,而不需要添加额外的 if-else 语句块,耦合性瞬间降低,每一种情况对应的处理方法也能瞬间定位非常方便。

回调函数的使用

 回调函数这种东西,喜欢的人非常喜欢,不喜欢的人也非常不喜欢,但这里我并不是讨论它的好还是不好,而是讨论它如何帮我简化代码。这里我们拿一个大家都很熟悉的场景来举例,Ajax。Ajax的操作的主要步骤:获取XHR对象,设置相关参数,发送请求,注册事件。代码如下

var xhr = getXHR()xhr.open(url, "get", true)xhr.send(null)xhr.onreadystatechange = function() {    if (xhr.readyState == 4) {        if ((xhr.status >= 200 && shr.status < 300) || xhr.status == 304) {            // do something        }    }}

一个前端页面中可能需要进行多个Ajax请求,像这个小型项目都使用了十多个Ajax请求用于请求不同的资源,如果我们每一次请求都写这么一段代码的话,代码的冗余性很高,如果不这样写的话如何处理每一个请求资源成功后的// do something 呢?如果使用策略模式,可能需要编写一堆映射,handleMap 会膨胀的不行,而且每一个的处理好像并不存在条件判断的逻辑。这时候我们就可以使用回调函数进行封装,在这里我们只封装两个参数,也就是 urlcallback,调用的时候只需要将url传入和请求成功后的处理函数作为回调函数传入即可,代码如下

// 封装Ajax请求var AjaxRequest = function(url, callback) {    var xhr = createXHR()    xhr.get(url, "get", true)    // 当服务器返回正确信息的时候执行 successcallBack 回调    xhr.onreadystatechange = function() {        if (xhr.readyState == 4) {            if ((xhr.status >= 200 && shr.status < 300) || xhr.status == 304) {                callback()            }    }    }}// 调用AjaxRequest(url, function(){    // do something})

通过这种方法就能够很轻易的将请求过程的共同性和时间的特异性封装起来,降低了耦合性,使得代码更加紧凑。当我们吧这部分和策略模式结合起来我们就可以写出根据请求类型(get 或者 post)来进行处理的函数,贴出我写的 util 类中和Ajax请求有关的代码

var util = (function() {    var o = new Object()    // 获取 XHR 对象     // ——《JavaScript高级程序设计》    var createXHR = function() {        if (typeof XMLHttpRequest != "undefined") {            return new XMLHttpRequest()        } else if (typeof ActiveXObject != "undefined") {            if (typeof arguments.callee.activeXString != "string") {                var versions = [                        "MSXML.XMLHttp.6.0",                        "MSXML2.XMLHttp.3.0",                        "MSXML2.XMLHttp"                    ],                    i,                    len                for (i = 0; (len = versions.length); i++) {                    try {                        new ActiveXObject(versions[i])                        arguments.callee.activeXString = versions[i]                        bresk                    } catch (ex) {                        console.log(ex)                    }                }            }            return new ActiveXObject(arguments.callee.activeXString)        }    }    var splitParams = function(url) {        var paramstart = url.indexOf("?")        if (paramstart == -1) {            return        }        return url.substring(paramstart)    }    // 处理 get 和 post 操作    var handleConnect = {        get: function(xhr, url) {            xhr.open("get", url, true)            xhr.send(null)        },        post: function(xhr, url) {            var params = splitParams(url)            xhr.open("post", url, true)            xhr.setRequestHeader(                "Content-type",                "application/x-www-form-urlencoded"            )            xhr.send(params)        }    }    // 为 url 添加参数    o.addUrlParam = function(url, paramsMap) {        for (var k in paramsMap) {            url += url.indexOf("?") == -1 ? "?" : "&"            url +=                encodeURIComponent(k) + "=" + encodeURIComponent(paramsMap[k])        }        return url    }    // 获取服务器返回的信息    o.AjaxRequest = function(url, connectType, successcallBack) {        var xhr = createXHR()        // 获取 get 或 post 的处理函数并执行        handleConnect[connectType](xhr, url)        // 当服务器返回正确信息的时候执行 successcallBack 回调        xhr.onreadystatechange = function() {            if (xhr.readyState == 4) {                if (                    (xhr.status >= 200 && xhr.status < 300) ||                    xhr.status == 304                ) {                    successcallBack(xhr)                }            }        }    }    return o})()

有需要的也可以补充,当请求进行中的回调,当请求失败时的回调,只需要将 successcallbackcallbackMap 替代,然后根据不同的情况提取相应的 callback

DOM操作和事件委托

 DOM操作和事件处理是JavaScript知识体系中的重要部分,只要是做JavaScript的就避不开这两部分。先聊一聊DOM操作,现在的前端应用如果使用Ajax的话就会有根据服务器返回数据生成DOM的需求,一般进行DOM操作有两种方法:

  1. 直接改写元素的 innerHTML
  2. 使用DOM方法,也就是 document.createElement()el.appendChild()

这两种方法的性能其实相差无几,所以我选择了编程性更好的DOM方法作为DOM操作的基础,所以每次进行DOM操作都会出现以下冗长的代码

var parent = document.querySelector("#parent")var el = document.createElement("div")el.setAttribute("attr1", "value1")el.setAttribute("attr2", "value2")......parent.appendChild(el)

我们可以抽象一下这个过程,创建一个节点元素,设置若干属性,把新创建的节点添加到父节点中,我们还需要获取新建节点的引用,可以得出以下代码

var addElement = function(tagName, params, parent){    var el = document.createElement(tagName)    for(var k in params) {        el.setAttribute(k, params[k])    }    parent.appendChild(el)    return el}// 调用var parent = document.querySelector("#parent")var el = addElement("div", {attr1: "value1", attr2: "value2"...}, parent)

DOM操作就封装好了,接下来我们谈一下,事件委托。事件委托是根据DOM的事件冒泡和事件捕获原理,给外层元素绑定一个处理器,就可以处理其子元素上触发的所有事件。这种技术是很实用的,例如当我们的点播页面有100条视频缩略图及相关信息,按照传统的办法就是给这100条信息添加事件处理器,那么你的页面在 onload 的时候就需要加载100个事件处理器,浏览器还需要对这些事件处理器进行监听,极其浪费资源。示例代码如下

var ul = document.querySelector("#ul")ul.addEventListener("click", function(event0) {    var target = event.target    if(condition(target)){        // do something    }})

总结

 这篇文章主要总结了我在重构最近写的弹幕播放器的时候所用到的相关技术,关于弹幕播放器的重构还在继续,也可能会用到其他相关的技术,之后也会记录下来和分享吧。

原创粉丝点击