知识共享许可协议

JavaScript 代码静态质量检查

自鸿蒙初判,Brendan Eich 10 天捏出 Mocha 之后,即便进化成 ECMAScript,这个语言依旧毁誉相随。那些经过重重劫难,侥幸渡劫成功的苦主标识了诸多天坑(见 JavaScript Garden) —— 当然,你也可以称之 feature。据无责任乱猜,Douglas Crockford 也没少踩坑,于是才有了蝴蝶书《JavaScript: The Good Parts》,下雨天与 JSLint 一起使用会更配哟。

《JavaScript: The Definitive Guide》 V.S. 《JavaScript: The Good Parts》

时至今日,代码的静态质量检查在项目质量保障方面的重要性与必要性已毋庸置疑。越来越多的开发者意识到了这一点,纷纷在项目构建流程或者源码控制系统中添加静态检查的 hook。本文将依时间顺序,选出 JavaScript 史上的主要几个 Linter 作横向比较,最终属意谁家,那就见仁见智了。

阅读全部

扁平化箭形代码

作为《嵌套条件的重构》 的姊妹篇,补充说明箭形代码的缺点,并以函数分解的方式扁平化箭形代码。

原文:Flattening Arrow Code

经常看到这种代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (rowCount > rowIdx) {
if (drc[rowIdx].table.columns.contains("avalId")) {
do {
if (attributes[attrVal.attributeClassId] == null) {
// do stuff
}
else {
if (!Array.isArray(attributes[attrVal.attributeClassId])) {
// do stuff
}
else {
if (!isChecking) {
// do stuff
}
else {
// do stuff
}
}
}
rowIdx++;
}
while (rowIdx < rowCount && parseInt(drc[rowIdx], 10) === Id);
}
else {
rowIdx++;
}
}
return rowIdx;

太多的条件嵌套使代码变成一个箭头的样子:

1
2
3
4
5
6
7
8
9
if
if
if
if
do something
endif
endif
endif
endif

当你在1280x1024 分辨率下阅读代码时会超出右边边界。这就是箭头反模式。

我重构的首要任务之一是把这样的箭形代码 “扁平化”。那些锋利的尖钩很危险!箭形代码有着很高的圈复杂度 -- 衡量贯穿代码有多少不同路径:

研究表明程序的质量与它的圈复杂度(WIKI, 百度百科)有关。低圈复杂度的程序更简单易懂,修改时的风险也更低。模块的圈复杂度与它的可测试性也是高度相关的。

在适当的地方,我通过以下方式扁平化箭形代码:

  1. 替换条件为 guard clauses,这个代码..
1
2
3
if (SomeNecessaryCondition) {
// function body code
}

.. 改成 guard clause 会更好:

1
2
3
4
if (!SomeNecessaryCondition) {
throw new RequiredConditionMissingException;
}
// function body code
  1. 用函数来分解条件块。在上例中,我们把 do..while 循环里的条件分解。
1
2
3
4
5
do {
validateRowAttribute(drc[rowIdx]);
rowIdx++;
}
while (rowIdx < rowCount && parseInt(drc[rowIdx], 10) === Id);
  1. 将否定检查转为肯定检查。主要规则是把肯定比较置前,让否定比较自然落到 else 中。我认为这样可读性肯定更好,更重要的是,避免 “我永远不会不做” 句式。
1
2
3
4
5
6
if (Array.isArray(attributes[attrVal.attributeClassId])) {
// do stuff
}
else {
// do stuff
}
  1. 总是尽快从函数返回。一旦工作完成,马上退出。这个并非永远合适的 -- 你可能需要清理资源。但无论如何,你必须放弃只应在底部有一个出口的错误想法。

目的是让代码在垂直方向滚动多些...而不是在水平方向上。

position:sticky实现iOS6+下的粘性布局

用户总是希望在窗口明显的地方方便找到自己想要的操作项,例如停留在浏览器窗口顶端的菜单栏,筛选栏等。所以我们会把用户最常用到或者我们希望用户注意到的内容一直展现在窗口的可视区域,让用户能够一眼就看到。这当中常需要用到一种页面滚动然后元素固定在窗口的某个位置的布局方式(下面简称粘性布局)。

要实现这种粘性布局,我们常常都是通过js来模拟,实现方案是通过js监听window的scroll事件,当需要固定的元素滚动到窗口顶部时,把元素的position属性设置为fixed,否则,取消fixed,简单的js代码如下:

1
2
3
4
5
6
var nav = document.querySelector('.nav');
var navOffsetY = nav.offsetTop;
function onScroll(e) {
window.scrollY >= navOffsetY ? nav.classList.add('fixed') : nav.classList.remove('fixed');
}
window.addEventListener('scroll', onScroll);

上面的实现在桌面浏览器的表现还是挺好的,但是在移动端浏览器上粘顶的效果就显得不那么平滑了。

阅读全部

数字知多少?

某天早上阳光明媚,挺风和日丽的,刚嚼一口早餐就被同学问到一个奇怪的问题,大概情况是这样的:

1
2
3
4
5
var str = '7172328d6ddf0296e7e7d4a8';
var n = parseInt(str, 16);

// false ...
console.log(n.toString(16) === str);

一个巨大的16进值字符串转化成数字后再转化成对应的字符串就不相等了,实际输出的值还差得蛮远的... 这个问题的第一反应就是精度丢失啰,不过同学们可不是这么好打发的,都不说一个为什么怎能了事呢?那就让我们稍微挖一挖,看看究竟是为什么吧~

阅读全部

1px on retina

一直以来我们实现边框的方法都是设置 border: 1px solid #ccc,但是在retina屏上因为设备像素比的不同,边框在移动设备上的表现也不相同:1px可能会被渲染成1.5px, 2px, 2.5px, 3px....,在用户体验上略差,所以现在要解决的问题就是在retina屏幕实现1px边框。

如果你去google类似问题,诚然会找到所谓的”答案“,然后很开森的用到项目中了。运气好的话,Yeah成功模拟1px了,运气不好了可能遇到各种奇葩的表现让你抓狂。

这篇文章总结了目前常用的模拟1px的方法,并分析各个方法的利弊。

阅读全部

CSS硬件加速的好与坏

本文翻译自Ariya Hidayat的Hardware Accelerated CSS: The Nice vs The Naughty。感谢Kyle He帮助校对。

每个人都痴迷于60桢每秒的顺滑动画。为了实现这个顺滑体验现在用的最流行的一个做法就是使用『CSS硬件加速』。在一些极端例子中,强制使用translate3d意味着大大提高应用程序的性能。

现代浏览器大都可以利用GPU来加速页面渲染。在GPU的众多特性之中,它可以存储一定数量的纹理(一个矩形的像素点集合)并且高效地操作这些纹理(比如进行特定的移动、缩放和旋转操作)。这些特性在实现一个流畅的动画时特别有用。浏览器不会在动画的每一帧都绘制一次,而是生成DOM元素的快照,并作为GPU纹理(也被叫做层)存储起来。之后浏览器只需要告诉GPU去转换指定的纹理来实现DOM元素的动画效果。这就叫做GPU合成,也经常被称作『硬件加速』。

不幸的是,浏览器是一个很复杂的软件(Firefox有几百万行代码)。因此一句简单的『使用translate3d来提高性能』并不能囊括所有的情况。如果碰巧有效那不过是瞎猫碰上死耗子而已。所以有必要知道更多的运行机制,才能更好地处理实际情况。

阅读全部