☰
jstips 实战指南:三个实用的 JavaScript 开发技巧——反向取数组、短路条件句与默认值设置
2026/10/8 13:58:35 网站建设 项目流程
  • 教程

【免费下载链接】jstips

This is about useful JS tips!

项目地址:https://gitcode.com/gh_mirrors/js/jstips
点击查看免费下载

本篇是 jstips 仓库中 2016-10-28-three-useful-hacks.md(第 60 期技巧)的深度解读。原文档分享了三个高频、轻量的语法糖:用slice(-n)从后向前读取数组、用&&短路执行条件函数、用||为变量设置默认值。读完本文,你将理解这三个技巧的底层运行原理、边界情况与常见陷阱,并能结合函数默认参数、空值合并运算符等现代写法做出正确的选型,在日常业务代码中写出更简洁、更不易出错的表达式。

三个技巧解决什么问题

JavaScript 开发者每天都在处理三件小事:取数组元素、做条件分支、赋默认值。这三个操作本身并不难,但写法却有好坏之分。jstips 第 60 期(英文原版见 _posts/en/javascript/2016-10-28-three-useful-hacks.md)给出的三个写法,共同特点是:

  • 更短:用表达式替代语句,减少括号与换行;
  • 更聚焦:把"取尾部元素""满足条件才执行""无值则用默认值"这类意图直接表达出来;
  • 依赖语言内建语义:不引入任何库,基于Array.prototype.slice、&&、||的自带行为。

下面逐一展开,并在每个技巧中补充原理、边界与仓库内其他期数的佐证。

技巧一:用slice(-n)从后向前获取数组元素

原文档给出的代码:

var newArray = [1, 2, 3, 4] console.log(newArray.slice(-1)) // [4] console.log(newArray.slice(-2)) // [3, 4] console.log(newArray.slice(-3)) // [2, 3, 4] console.log(newArray.slice(-4)) // [1, 2, 3, 4]

为什么-n表示"从尾部开始"

Array.prototype.slice(begin, end)返回从begin到end(不含)之间的新数组。当传入的索引为负数时,语言规范会将其换算为length + 索引,例如slice(-1)等价于slice(3),slice(-2)等价于slice(2)。因此:

  • slice(-1)→ 取最后 1 个元素[4];
  • slice(-2)→ 取最后 2 个元素[3, 4];
  • slice(-n)的通式即"从倒数第 n 个元素开始,一直取到数组末尾"。

边界情况与等价写法

  • n超过数组长度:例如对[1, 2, 3, 4]执行slice(-10),换算后是负数索引,会被当作0处理,返回整个数组[1, 2, 3, 4],不会报错;
  • n为 0:slice(-0)即slice(0),返回完整副本,注意不要用它来"取 0 个尾部元素";
  • slice是非破坏性的:它返回新数组,原数组newArray保持不变。这点与pop()(取出并移除最后一个元素)、length = 0(清空数组)完全不同——关于后者,可参考仓库中 2016-01-22-two-ways-to-empty-an-array.md 对list = []与list.length = 0内存语义差异的剖析。

实战场景

  • 读取最近 N 条记录:如聊天列表取最近 5 条,messages.slice(-5);
  • 日志尾部查看:logs.slice(-50)只展示最新 50 行,避免复制整个数组;
  • 环形/滚动数据结构:配合concat做分页式尾部追加,可参考仓库中 2016-02-06-deduplicate-an-array.md 对数组复制与引用语义的讨论,理解为什么非破坏性操作更适合数据处理链路。

技巧二:短路条件句condition && dosomething()

原文档给出对比写法:

// 常规写法 if (condition) { dosomething() } // 短路写法 condition && dosomething()

短路求值的本质

&&是短路求值运算符:只有当第一个操作数无法决定整个表达式的结果时,才会对第二个操作数求值。具体来说:

  • 若condition为 falsy,&&的结果已经确定为该 falsy 值,dosomething()根本不会被调用;
  • 若condition为 truthy,才继续求值并调用dosomething()。

这正是"当条件为真时才执行"的语义,只不过把if语句压成了一行表达式。仓库中的 2016-01-27-short-circuit-evaluation-in-js.md 对这一机制有完整讲解:当&&第一个操作数为false时结果必为false,当||第一个操作数为true时结果必为true,因此第二个操作数可以跳过。

注意:&&返回的是操作数,而不是布尔值

condition && dosomething()的返回值分两种情况:condition 为 falsy 时返回 condition 本身,否则返回dosomething()的返回值。因此:

  • 不要把这个表达式的结果直接当作"布尔值"使用;
  • 如果dosomething()返回undefined,整个表达式结果也是undefined;
  • 若要把结果统一成布尔值,可叠加!!,即!!(condition && dosomething())。!!的 truthy/falsy 强制转换规则参见仓库中 2016-01-30-converting-truthy-falsy-values-to-boolean.md。

两个高频变体

1. 安全调用,避免 "Cannot read property of undefined"

&&常被用来在对象可能为空时安全访问属性,这正是 2016-01-27-short-circuit-evaluation-in-js.md 中演示的经典模式:

var dog = { bark: function() { console.log('Woof Woof'); } }; dog.bark(); // dog 未定义时会抛错 dog && dog.bark(); // 仅当 dog 存在时才调用,安全

2. 回调函数保护

Node.js 风格的函数中,回调参数可能为空,直接用callback(err)会抛 TypeError。用短路写法即可优雅兜底,这一模式在仓库的 2016-05-06-use-optional-arguments.md 中反复出现:

if (err) { return callback && callback(err); }

与if语句的取舍

  • condition && dosomething()适合单条、无返回值依赖的调用,让代码更紧凑;
  • 当需要多个语句、else分支或较复杂的逻辑时,应回归if语句,避免可读性下降;
  • 条件为false时若你本意是"执行另一个函数",可写成!condition && dosomethingElse(),但两个并列的短路表达式不如if/else直观。

技巧三:用||为变量设置默认值

原文档给出的代码:

var a console.log(a) // undefined a = a || 'default value' console.log(a) // default value a = a || 'new value' console.log(a) // default value

运行原理

||同样遵循短路求值:若第一个操作数为 truthy,直接返回它,不再看第二个;若第一个操作数为 falsy,则返回第二个操作数。于是a = a || 'default value'的含义是:若a已经有值,保持原值;若a为空(falsy),则赋默认值。由于'default value'是 truthy,第二次a = a || 'new value'中a已非空,结果仍是'default value',原文档的输出恰好印证了这一点。

最大的陷阱:falsy 值不止undefined

||判断的是 truthy/falsy,而不是"是否为 null/undefined"。因此下面这些合法且常见的值也会被默认值替换:

var count = 0; count = count || 10; // 结果变成 10,而你可能期望保留 0 var name = ''; name = name || '匿名'; // 空字符串被替换 var ratio = NaN; ratio = ratio || 1; // NaN 被替换 var flag = false; flag = flag || true; // false 被替换成 true,逻辑反转!

falsy 值的完整集合是false、0、-0、0n、''、null、undefined、NaN(可对照 2016-01-30-converting-truthy-falsy-values-to-boolean.md 中的!!示例)。所以当业务上"0、空字符串、false"都是合法值时,||并不合适。

更精准的替代方案

  • 函数参数默认值(ES6):仅在参数为undefined时生效,0、''、false会原样保留:
function greet(name = '访客') { return 'Hello, ' + name; } greet(); // Hello, 访客 greet(''); // Hello, greet('Tom'); // Hello, Tom
  • 空值合并运算符??(ES2020):只对null和undefined兜底,适合"数字 0、空字符串是有效值"的场景:
var count = 0; count = count ?? 10; // 0,保留 var maybeNull = null; maybeNull = maybeNull ?? 10; // 10
  • 需要区分undefined与null时,可参考仓库 2016-01-05-differences-between-undefined-and-null.md 的总结:undefined表示"未赋值",null表示"显式的空值",两者用==相等但用===不等。

函数入参默认值的经典用法

||给函数参数设默认值是最常见的实战场景,仓库 2016-01-27-short-circuit-evaluation-in-js.md 中就有完整示例:

function theSameOldFoo(name){ name = name || 'Bar'; console.log("My best friend's name is " + name); } theSameOldFoo(); // My best friend's name is Bar theSameOldFoo('Bhaskar'); // My best friend's name is Bhaskar

注意同样的陷阱:如果name的合法值可能是空字符串或0,请改用上面的 ES6 默认参数或??。

三个技巧对照小结

技巧写法适用场景必须注意
反向取数组arr.slice(-n)取尾部 N 个元素、最近记录非破坏性;n=0返回整个副本
短路条件执行condition && fn()条件为真才执行、安全访问、回调保护表达式结果是操作数而非布尔值
||设默认值a = a \|\| 'default'参数/变量为空时兜底0、''、false、NaN都会被替换

从仓库结构看,这三个技巧并非孤例:短路求值(第 27 期)、truthy/falsy 转换(第 30 期)、可选参数与回调保护(第 54 期)等文档都在围绕同一套"表达式即控制流"的 JavaScript 内建语义展开,说明它们是高频且值得熟练掌握的基础能力。建议在团队代码中为这三个写法约定统一的边界(如:业务上允许0/空字符串时禁用||兜底),避免语法糖带来的隐式 bug。

延伸阅读(仓库内)

  • 2016-01-27-short-circuit-evaluation-in-js.md:短路求值完整原理与安全访问示例
  • 2016-01-30-converting-truthy-falsy-values-to-boolean.md:truthy/falsy 值与!!强制转换
  • 2016-01-05-differences-between-undefined-and-null.md:undefined与null的判断与区别
  • 2016-05-06-use-optional-arguments.md:可选参数与callback && callback(err)的实战模式
  • 2016-01-22-two-ways-to-empty-an-array.md:数组引用语义与length = 0的说明
  • 2016-02-06-deduplicate-an-array.md:基于filter、Set的数组去重与引用语义分析
  • 教程

【免费下载链接】jstips

This is about useful JS tips!

项目地址:https://gitcode.com/gh_mirrors/js/jstips
点击查看免费下载

相关推荐

上一篇:3分钟掌握fzf的zsh自动补全:效率提升10倍的秘密
下一篇:fzf shell集成:提升终端效率的完整方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询