浅谈JavaScript应用重构

浅谈JavaScript应用重构

前言

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

设计模式之策略模式

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

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

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

// 情况和处理函数Map
var 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
    }
})

总结

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
重构JavaScript的高阶函数,可以考虑以下几个方面: 1. 使用箭头函数:箭头函数提供了简洁的语法,可以让代码更加清晰易读。将普通函数转换为箭头函数可以减少冗余的代码,并且更好地展示函数的意图。 例如,将普通函数重构为箭头函数: ```javascript // 普通函数 function square(x) { return x * x; } // 重构为箭头函数 const square = x => x * x; ``` 2. 函数柯里化(Currying):将一个接受多个参数的函数转换为一系列只接受单个参数的函数。这种转换可以使函数更加灵活,便于复用和组合。 例如,将一个接受多个参数的函数重构为柯里化函数: ```javascript // 普通函数 function add(x, y) { return x + y; } // 重构为柯里化函数 const add = x => y => x + y; ``` 3. 使用高阶函数:高阶函数接受一个或多个函数作为参数,并返回一个新的函数。通过使用高阶函数,可以将一些通用的逻辑抽象出来,减少代码的重复性。 例如,使用高阶函数重构过滤数组的逻辑: ```javascript // 普通函数 function filterArray(array, predicate) { const result = []; for (let i = 0; i < array.length; i++) { if (predicate(array[i])) { result.push(array[i]); } } return result; } // 重构为高阶函数 const filterArray = (array, predicate) => array.filter(predicate); ``` 以上是一些常见的重构技巧,当然具体的重构方式还需要根据实际情况来确定。重构的目标是提高代码的可读性、可维护性和可重用性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若即

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值