Skip to content

愚人节特供:一些 JavaScript 奇技淫巧

Published: at 00:00编辑

这已经成为每年的传统了,挑一些奇奇怪怪的方法出来讲一讲。你可以在这里阅读去年的 JavaScript 奇技淫巧

0x00 开胃菜:用字符串返回一个键盘图形

具体源码已经在很多文章里出现过了,简单整理贴在下方:

console.log(
  ((_) =>
    [..."`1234567890-=~~QWERTYUIOP[]\\~ASDFGHJKL;'~~ZXCVBNM,./~"].map(
      (x) =>
        ((o += `/${(b = "_".repeat((w = x < y ? 2 : " 667699"[((x = ["BS", "TAB", "CAPS", "ENTER"][p++] || "SHIFT"), p)])))}\\|`),
        (m += y + (x + "    ").slice(0, w) + y + y),
        (n += y + b + y + y),
        (l += " __" + b))[73] &&
        (k.push(l, m, n, o), (l = ""), (m = n = o = y)),
      (m = n = o = y = "|"),
      (p = l = k = []),
    ) &&
    k.join`
`)(),
);

这里我花了一个多小时研究了一下这份源码,猜测可能是 JavaScript Golf 等活动的参赛作品(如有知道的烦请评论补充),下面贴上我的拆分代码:

/**
 * 变量名命名由 AI 驱动,但是 AI 解释写出来的代码完全不能用
 *
 * specialKeyIndex 特殊按键索引
 * currentKey 当前处理的按键字符
 * currentFill 当前按键的填充部分
 * keyWidth 按键宽度
 * separatorChar 分隔符字符
 * currentUpperBorder 上边框构建
 * currentMiddleContent 中间内容层
 * currentLowerBorder 下边框层
 * currentLine 当前行缓存
 * resultLines 最终结果行数组
 */
console.log(
  ((_) =>
    [..."`1234567890-=~~QWERTYUIOP[]\\~ASDFGHJKL;'~~ZXCVBNM,./~"].map(
      (currentKey) =>
        ((currentUpperBorder += `/${(currentFill = "_".repeat(
          (keyWidth =
            currentKey < separatorChar
              ? 2
              : " 667699"[
                  ((currentKey =
                    ["BS", "TAB", "CAPS", "ENTER"][specialKeyIndex++] ||
                    "SHIFT"),
                  specialKeyIndex)
                ]),
        ))}\\|`),
        (currentMiddleContent +=
          separatorChar +
          (currentKey + "    ").slice(0, keyWidth) +
          separatorChar +
          separatorChar),
        (currentLowerBorder +=
          separatorChar + currentFill + separatorChar + separatorChar),
        (currentLine += " __" + currentFill))[73] &&
        (resultLines.push(
          currentLine,
          currentMiddleContent,
          currentLowerBorder,
          currentUpperBorder,
        ),
        (currentLine = ""),
        (currentMiddleContent =
          currentLowerBorder =
          currentUpperBorder =
            separatorChar)),
      (currentMiddleContent =
        currentLowerBorder =
        currentUpperBorder =
        separatorChar =
          "|"),
      (specialKeyIndex = currentLine = resultLines = []),
    ) &&
    resultLines.join`
`)(),
);

生成 1-100 的数组

const arr1 = [...Array(100).keys()];
// ref: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/keys

数组去重

const arr = [1, 1, 4, 5, 1, 4, 1, 9, 1];
const newArr = [...new Set(arr)]; // => (4) [1, 4, 5, 9]
/* 或者使用 .reduce() */
const anotherArr = arr.reduce(
  (prev, cur) => (prev.includes(cur) ? prev : [...prev, cur]),
  [],
);
// ref: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

获取数组中的最大值和最小值

let num = [1, 1, 4, 5, 1, 4, 1, 9, 1];
let max = Math.max(...num); // => 9
let min = Math.min(...num); // => 1

调整数组大小或清空数组

let array = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"];
console.log(array.length); // => 10
/* 让我们调整一下数组长度.. */
array.length = 4;
console.log(array.length); // => 4
console.log(array); // => ['a', 'b', 'c', 'd']

删除数组中的假值

let num = [1, 1, 4, 5, 1, 4, 1, 9, 1, null, 0, undefined];

num.filter(Boolean); // => (9) [1, 1, 4, 5, 1, 4, 1, 9, 1]

字符串求反转

const str = "114514"
[...str].reverse().join('') // => '415411'

|| 设置默认值

/* 开发经常使用,算不上奇技淫巧,所以请看下面那个 */
let userInput = input || "默认值";

空值合并运算符

替代 || 的更精准的默认值赋值。

const value = a ?? "default"; // 如果 a 为 null/undefined,取 'default'

字符串转数字

这里使用了一元加运算符(Unary Plus Operator),作用是将操作数显式转换为数字类型。如果无法解析,返回将是 NaN

let str = "114514";
console.log(+str); // => 114514
typeof +str; // => 'number'

小数取整

let num = 114.514;

parseInt(num); // => 114
/* 按位或运算符(Bitwise OR Operator) */
num | 0; // => 114
/* 两个按位非运算符 ~ 的连续使用,按位非运算符 Bitwise NOT Operator */
~~num; // => 114
/* 左移运算符(Bitwise Left Shift Operator) */
num << 0; // => 114
/* 按位异或运算符(Bitwise XOR Operator) */
num ^ 0; // => 114

当你正在处理正数,~~Math.floor 的替代方案,它速度更快且占用的字符更少。

本部分参考链接:JavaScript 的双位非运算(~~)

检查对象是否为空

Object.keys(objectName).length; // 如果返回 0 代表这个对象是空的

使用 Object.is() 比较对象

处理 NaN 和不同零值的精确比较。

Object.is(1, 1); // => true
Object.is(NaN, NaN); // => true(而 === 返回 false)
Object.is(-0, 0); // => false(而 === 返回 true)

可选链操作符 ?

通常用于安全访问和安全调用方法,可以和结合空值协同运算符 ?? 结合。

const user = { address: null };
console.log(user.address.city); // Cannot read properties of null (reading 'city')
console.log(user.address?.city); // => undefined

短路赋值

利用逻辑运算符的短路特性进行条件赋值。

let a;
(a = 5) && console.log(a); // 输出 5(若 a 为 falsy 则不执行)
let b = null;
b || (b = "default"); // b 变为 'default'

使用 Symbol 作为对象键,避免属性名冲突

const secretKey = Symbol("secret");
const obj = { [secretKey]: "value" };
obj[secretKey]; // 'value'

箭头函数的隐式返回

单行表达式可省略 return 和大括号。

const double = (x) => x * 2; // 等同于 x => { return x * 2; }

立即执行函数表达式的箭头写法

快去用箭头函数替代传统 IIFE

(() => {
  console.log("Hello");
})();
// 箭头写法:
(() => console.log("Hello"))();

Intl API 格式化数据

快速格式化数字和日期

// 数字格式化
new Intl.NumberFormat("en-US").format(1234567); // "1,234,567"

// 日期格式化
new Intl.DateTimeFormat("zh-CN").format(new Date()); // "2023/10/5"

Intl API 相对时间格式化

const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
rtf.format(-1, 'day'); // => "1 day ago"
rtf.format(2, 'hour'); // => "in 2 hours"

String.raw() 原始模板字符串处理

配合模板字符串,避免转义,常用于生成正则表达式或 JSON,这里有具体的详细使用案例

console.log(String.raw`Hello\nWorld`); // => "Hello\nWorld"(不转义)

无论是用 ~~ 替代 Math.floor 的性能优化,还是通过 String.raw 处理原始字符串的优雅方案,这些技巧共同印证了 JavaScript「简洁即力量」的设计哲学。特别在代码高尔夫(Code Golf)领域,开篇的键盘图形代码正是这种「用最少字符实现最大功能」精神的完美体现。

希望这些技巧能为你的开发旅程提供新视角——毕竟,编程不仅是解决问题,更是一场与代码美学的对话。期待明年愚人节,我们继续用创意代码点燃技术灵感!

文章中全部代码使用 WTFPL 开放授权,如您有任何问题请留言反馈。


下一篇
在 Debian 12 上安装 Pocket ID 配置单点登录 SSO 教程

人机验证:请刷新页面以加载评论区