聊聊jQuery的反形式

假如我们以为形式代表一个最好的实践,那末反形式将代表我们已学到一个经验。受启发于Gof的《设想形式》,Andrew Koeing在1995年的11月的C++报告大会上初次提出反形式。在Koeing的报告中,反形式有着两种看法:

  • 形貌关于一个特别的题目,提出了一个蹩脚的解决方案,终究致使一个坏效果发作

  • 形貌怎样挣脱上述解决方案并能提出一个好的解决方案

在如今这个前端生长热火朝天的时期,谈及jq老是显得异常的low,但实际上,在学校,在很多前端新人以及所谓“页面仔 || 切图工”之类的偕行之间,jq的生机照样远超种种框架的时刻,之所以想写如许一篇文章,一是由于见到了身旁的jq烂代码,二是由于我在百度jQuery反形式的时刻竟然什么有价值的相干效果都没有,所以以为照样有必要聊聊的。

先从些简朴的最先:

插进去DOM节点:

// 反形式
$.each(reallyLongArray, function(count, item) {
    var newLi = '<li>' + item + '</li>';
    $('#ballers').append(newLi)
})

// 更好的实践
var frag = document.createDocumentFragment()
$.each(reallyLongArray, function(count, item) {
    var newLi = '<li>' + item + '</li>';
    frag.appendChild(newLi[0])
})
$('#ballers')[0].appendChild(frag)

// 你也能够用字符串
var myHTML = ''
$.each(reallyLongArray, function(count, item) {
    myHTML += '<li>' + item + '</li>';
})
$('#ballers').html(myHTML)

DocumentFragment是浏览器为了削减DOM操纵中的更新所运用的API,详情请查阅MDN相干文档。

遵照DRY准绳:

if ($a.data('current') != 'showing') {
    $a.stop()
}
if ($b.data('current') != 'showing') {
    $b.stop()
}
if ($c.data('current') != 'showing') {
    $c.stop()
}

// 用数组来保留差别的主体
var elems = [$a, $b, $c]
$.each(elems, function(k, v) {
    if (v.data('current') != 'showing') {
        v.stop()
    }
})

用数组或对象来保留反复片断的差别参数是一种很罕见的要领。更多内容能够参考罕见的JavaScript设想形式中的“九、战略形式”

地狱式回调(callback hell):

$(document).ready(function() {
  $('#button').click(function() {
    $.get('http://api.github.com/repos/facebook/react/forks', function(data) {
      alert(data[0].owner.login)
    })
  })
})

// 之前有这么一种优化的要领,运用对象字面量保留回调使其扁平化
var cbContainer = {
  initApp: function() {
    $(document).ready(cbContainer.readCb)
  },
  readyCb: function() {
    $('#button').click(cbContainer.clickCb)
  },
  clickCb: function() {
    $.get('http://api.github.com/repos/facebook/react/forks', function(data) {
      cbContainer.getCb(data)
    })
  },
  getCb: function(data) {
    alert(data[0].owner.login)
  }
}

cbContainer.initApp()

// 不过如今盛行Promise

var initApp = function() {
  return new Promise(function(resolve, reject) {
      $(document).ready(resolve)
  })
}

var readyCb = function() {
    return new Promise(function(resolve, reject) {
        $('#button').click(resolve)
    })
}

var clickCb = function() {
    return new Promise(function(resolve, reject) {
        $.get('http://api.github.com/repos/facebook/react/forks', function(data) {
      resolve(data)
    })
    })
}

var getCb = function(data) {
    alert(data[0].owner.login)
}

initApp()
  .then(readyCb)
  .then(clickCb)
  .then(getCb)

用对象将回调扁平还好,Promise是什么鬼。不是比回调还恶心,好吧,示例确切是如许。实在之所以用Promise除了将回调转成链式挪用之外,重要照样为了用它的reject函数获取回调中的毛病。像示例这类一起resolve的,没必要这么用。这里只是提一句。

假如愿望相识更多关于回调相干的学问,能够看看Promise, generator, async与ES6这篇文章。

反复查询:

$(document.body).append('<div class="baaron"></div>')
$('.baaron').click(function() {})

// 更好的体式格局
$('<div class="baaron"></div>')
    .appendTo(document.body)
    .click(function() {})

选择器:

关于jq的选择器另有很多要注意的题目,由于jq的选择器是从右向左查询,所以请记着一个“左轻右重”的准绳:

// 请看下面两个选择器
$('div.foo .bar')
$('.foo span.bar')        //右侧更明白一点,会好不少

// 当左侧确切要比右侧明白的时刻这么干
$('#foo .bar')
$('#foo').find('.bar')

// 特别防止运用通配符
$('#foo > *')
$('#foo').children()

// 有些通配符是隐式的
$('.foo :radio')
$('.foo *:radio')        //和上边一样的
$('.foo input:radio')    //改成如许

聊聊依靠:

接下来,让我们从优化一段jq代码最先,聊聊js中的依靠

$('#button').click(function() {
    $.get('http://xxxx', function(data) {
        $('#page').html(data.abc)
    })
})

这段代码有以下题目:

  • click事宜绑定的匿名函数难以反复运用,也很难测试

  • click回调的匿名函数中的$是全局变量,ajax要求回调的匿名函数中的$(‘#page’)也是用到了$这一全局变量,全局变量应该是要防止的

  • 回调的题目前面也说过了,这里的回调还很清楚不至于说到地狱的水平

如今我们把代码如许改写:

var downJSON = function() {
    $.get('http://xxxx', function(data) {
        $('#page').html(data.abc)
    })
}

$('#button').click(downJSON)

如今匿名函数被我们拿出来了,能够重用了,但照样难以测试,且涵盖全局变量。

继承:

var downJSON = function($, $el) {
    $.get('http://xxxx', function(data) {
        $el.html(data.abc)
    })
}

$('#button').click(function() {
    downJSON($, $('#page'))
})

如许改写今后,没有了全局变量,函数已自力出去。换一种说法就是,我们去除了函数中的隐式依靠(前面例子中的函数要运转须要全局变量$,但没有从函数声明中表现出来,我们称其为隐式依靠),如今,函数实行所须要的依靠被显现声明,使其具有更好的可控性。前端的依靠治理如今是一个很盛行的话题,不过在这里就不空话了。

奇技淫巧:

末了,关于几种比较罕见的写法,我们也能够运用一些奇技淫巧,或能使代码更短,或能使代码更加易读:

简化前提语句:

// 罕见的写法
if(!data) {
    data = {}
}

// 简化
data = data || {}

你能够以为这何足道哉,但能够有些时刻你写着写着就无视了。比方,js数组去重的4个要领中的第二个要领,就能够运用这个技能:

// 本来的代码
Array.prototype.unique2 = function()
{
    var hashTable = {},res=[];                //n为hash表,r为暂时数组
    for(var i = 0; i < this.length; i++) {    //遍历当前数组
        if (!hashTable[this[i]]) {            //假如hash表中没有当前项
            res.push(this[i]);                //把当前数组确当前项push到暂时数组内里
            hashTable[this[i]] = true;        //存入hash表
        }
    }
    return res;
}

// 运用此技能
Array.prototype.unique2 = function()
{
    var hashTable = {}, res = []
    for(var i = 0; i < this.length; i++) {
        !hashTable[this[i]] ? res.push(this[i]) : null
        hashTable[this[i]] = hashTable[this[i]] || true
    }
    return res
}

写成如许也未必说是优化,目测推断逻辑还多了一个哈哈,然则嵌套少了一层,怎么说呢,自行决议吧。

下面展现的一个技能和上面这个也差不多:

// 一般写法
if(type === 'foo' || type === 'bar') {}

// 用对象有三种要领
// 要领1
if(({foo: 1, bar: 1})[type]) {}                    // type === 'toString'能够绕过考证

// 要领2
if(type in ({foo: 1, bar: 1})) {}                // 和上一种等价然则慢点

// 要领3
if(({foo: 1, bar: 1}).hasOwnProperty(type)) {}    // 最周密,也最慢

// 用正则
if(/^(foo|bar)$/.test(type)) {}

这类技能的话,运用与否同样是本身决议。很多有意思的东西,都是本身想着玩的,有些状况,我们倒不如好好写成switch – case,都看的懂,也挺清楚。

总结两句,虽然说jQuery库和新式的框架比拟老了,但我以为它在DOM操纵上真的做到了一个极致。我置信很长一段时间,前端开发人员入门,照样要从它最先的。个人以为jq不适应时期的缘由,是由于它本身也仅仅限于DOM操纵,没有其他限定,以至于当运用庞杂时,你完整掌握不住你的页面。当你用上盛行的框架,根据他们的Best Practice去构造代码,我想你刚刚最先的时刻,一定会思念jQuery这个宠嬖你的老朋友的。

参考链接:

反形式 – 学用 JavaScript 设想形式 – 极客学院
jQuery Anti-Patterns for Performance & Compression
Patterns of Large-Scale JavaScript Applications

    原文作者:LeuisKen
    原文地址: https://segmentfault.com/a/1190000004538883
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞