这已经成为每年的传统了,挑一些奇奇怪怪的方法出来讲一讲。你可以在这里阅读去年的 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 开放授权,如您有任何问题请留言反馈。