本文不追求列举全部妈见打方法。

0x00 妈见打方法一

代码写的巨冗杂繁琐。

  • 例 1.

    1
    2
    3
    4
    5
    6
    7
    8
    function getWeekDayStr(day) {
    if(day === 1) {
    return "星期一";
    } else if(day === 2) {
    return "星期二";
    }
    // ...
    }

    这种代码一看就能缩减掉很多行:

    1
    2
    3
    function getWeekDayStr(day) {
    return "星期" + "一二三四五六日"[day - 1];
    }
  • 例 2.

    本段似乎使用了只能在 Mac/iOS/Android 系统上查看的 Emoji 表情符号 1,在 Windows 上可能显示为空心方框。

    这里需要注意,"①".length === 1"1️⃣".length === 3

    长度为 3 是因为 "1️⃣" === "1" + "\ufe0f" + "\u20e3"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function matchPrefix(str) {
    if(str.startsWith("①") || str.startsWith("1️⃣"))
    return 1;
    else if(str.startsWith("②") || str.startsWith("2️⃣"))
    return 2;
    else if(str.startsWith("③") || str.startsWith("3️⃣"))
    return 3;
    // ...
    }

    稍微高端点的写法:(还是很复杂)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function matchPrefix(str) {
    switch(true) {
    case str.startsWith("①"):
    case str.startsWith("1️⃣"):
    return 1;
    case str.startsWith("②"):
    case str.startsWith("2️⃣"):
    return 2;
    case str.startsWith("③"):
    case str.startsWith("3️⃣"):
    return 3;
    // ...
    }
    }

    简单点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /* 踩坑提示:如果别的程序员没注意1️⃣其实是三个字符的话,改成 /^[①1️⃣]/ 会导致极少数情况下出 Bug!
    * 实际上相当于想要实现 /^(a|bcd)/,结果搞成了 /^[abcd]/,当然就不对了
    * 例如:matchPrefix("1️⃣"[0]) 会返回 true(本应返回false)
    */
    function matchPrefix(str) {
    if(/^(①|1️⃣)/.test(str)) {
    return 1;
    } else if(/^(②|2️⃣)/.test(str)) {
    return 2;
    } // ...
    }

    但是,如果真的想最好地支持 Unicode,其实应该这么写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function matchPrefix(str) {
    "use strict"; // 因为要修改参数,这样可以不同时修改arguments,加速
    str = str.normalize();
    if(/^(①|1️⃣)/.test(str)) {
    return 1;
    } else if(/^(②|2️⃣)/.test(str)) {
    return 2;
    } // ...
    }
  • 例 3.

    我数学就是差,怎么地吧!

    1
    2
    3
    4
    5
    function getUnit(bytes) {
    let i = 1;
    while(1024 ** i <= bytes) i++;
    return ["bytes", "kb", "mb", "gb", "tb", "pb"][i-1];
    }

    稍微优化一下:

    1
    2
    3
    function getUnit(bytes) {
    return ["bytes", "kb", "mb", "gb", "tb", "pb"][Math.floor(Math.log(bytes)/Math.log(1024))];
    }

    第 2 行有点长,加个位运算来向下取整:

    1
    2
    3
    function getUnit(bytes) {
    return ["bytes", "kb", "mb", "gb", "tb", "pb"][~~(Math.log(bytes)/Math.log(1024))];
    }

    试试还能不能再短点:

    1
    2
    3
    4
    function getUnit(bytes) {
    return ["bytes", "kb", "mb", "gb", "tb", "pb"][~~(Math.log2(bytes)/10)];
    }
    // Math.log2(1024) === 10

    呵呵,其实已经有点优化过头了,就此打住吧。

0x01 妈见打方法二

代码写的巨晦涩难懂。

  • 例 1.

    1
    2
    3
    function dividedBy4(num) {
    return (num ^ (num << 2)) >>> 2 ^ num;
    }

    正常的写法:

    1
    2
    3
    function dividedBy4(num) {
    return Math.floor(num / 4);
    }

    其实不是完全等价的写法,因为最上面那个函数只能处理到 \([- 2^{31}, 2^{31})\)

  • 例 2.

    1
    2
    3
    function jsfuck() {
    console.log(([][[]]+[])[+[]]+([][[]]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]);
    }

    这特么是什么鬼???

    正常的写法:

    1
    2
    3
    function normal() {
    console.log("united");
    }

0x02 妈见打方法三

变量名瞎起、函数名拼音英文混杂,没有统一代码风格……

如何手动制造这类代码:

  1. 用 TypeScript 写一段面向对象、Promise和async/await还有yield混用的代码,转译成 ECMAScript 7
  2. 再用 Babel 转译成 ECMAScript 6
  3. 接着用 npm: browserify 或者是 gh: nathan/pax,打包成能直接在浏览器里运行的代码
  4. Google Closure Compiler 来压缩代码,选择压缩等级为 SIMPLE_OPTIMIZATIONS
  5. Prettier.io 美化回来

比如说我拿出一段之前写的js代码,按照这个方法稍微弄了下,弄出来了这玩意:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var __awaiter = undefined && undefined.__awaiter || function (l, k, e, f) { return new (e || (e = Promise))(function (c, h) { function d(a) { try { g(f.next(a)) } catch (m) { h(m) } } function b(a) { try { g(f["throw"](a)) } catch (m) { h(m) } } function g(a) { a.done ? c(a.value) : (new e(function (b) { b(a.value) })).then(d, b) } g((f = f.apply(l, k || [])).next()) }) }, __generator = function (l, k) {
function e(a) { return function (b) { return f([a, b]) } } function f(a) {
if (h) throw new TypeError("Generator is already executing."); for (; c;)try {
if (h = 1, d && (b = a[0] & 2 ? d["return"] : a[0] ? d["throw"] || ((b = d["return"]) &&
b.call(d), 0) : d.next) && !(b = b.call(d, a[1])).done) return b; if (d = 0, b) a = [a[0] & 2, b.value]; switch (a[0]) {
case 0: case 1: b = a; break; case 4: return c.label++ , { value: a[1], done: !1 }; case 5: c.label++; d = a[1]; a = [0]; continue; case 7: a = c.ops.pop(); c.trys.pop(); continue; default: if (!(b = c.trys, b = 0 < b.length && b[b.length - 1]) && (6 === a[0] || 2 === a[0])) { c = 0; continue } if (3 === a[0] && (!b || a[1] > b[0] && a[1] < b[3])) c.label = a[1]; else if (6 === a[0] && c.label < b[1]) c.label = b[1], b = a; else if (b && c.label < b[2]) c.label = b[2], c.ops.push(a); else {
b[2] && c.ops.pop();
c.trys.pop(); continue
}
}a = k.call(l, c)
} catch (m) { a = [6, m], d = 0 } finally { h = b = 0 } if (a[0] & 5) throw a[1]; return { value: a[0] ? a[1] : void 0, done: !0 }
} var c = { label: 0, sent: function () { if (b[0] & 1) throw b[1]; return b[1] }, trys: [], ops: [] }, h, d, b, g; return g = { next: e(0), "throw": e(1), "return": e(2) }, "function" === typeof Symbol && (g[Symbol.iterator] = function () { return this }), g
}, fs = require("fs"), path = require("path"), util_1 = require("util"), lstat = util_1.promisify(fs.lstat), stat = util_1.promisify(fs.stat), readdir = util_1.promisify(fs.readdir);
function dfs(l, k) {
return __awaiter(this, void 0, void 0, function () {
var e, f, c, h, d, b, g; return __generator(this, function (a) {
switch (a.label) {
case 0: e = [], f = 0, a.label = 1; case 1: if (!(f < k.length)) return [3, 9]; c = path.join(l, k[f]); return [4, lstat(c)]; case 2: return a.sent().isSymbolicLink() ? [3, 8] : [3, 3]; case 3: return [4, stat(c)]; case 4: if (!a.sent().isDirectory()) return [3, 7]; d = (h = e).concat; b = dfs; g = [c]; return [4, readdir(c)]; case 5: return [4, b.apply(void 0, g.concat([a.sent()]))]; case 6: return e = d.apply(h, [a.sent()]), [3,
8]; case 7: e.push(path.join(l, k[f])), a.label = 8; case 8: return f++ , [3, 1]; case 9: return [2, e]
}
})
})
} module.exports = dfs;

嗯,源代码刨去 jsDoc 注释和空行只有 21 行。

0x03 妈见打方法四

我觉得这种才是最可怕的,因为在生产环境看到这样的代码可能会心脏病发作

话不多说,上图

Reddit-ProgrammingHorror-3.png Reddit-ProgrammingHorror-2.png Reddit-ProgrammingHorror-4.jpg Reddit-ProgrammingHorror-5.png Reddit-ProgrammingHorror-1.png

来源:https://blog.jiejiss.com/