假如我们以为形式代表一个最好的实践,那末反形式将代表我们已学到一个经验。受启发于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