不加分号可能会使代码错误执行

故事背景

今天在刷leetcode46. 全排列的时候,出现了一个令人费解的问题

js 复制代码
var permute = function (nums) {
  const res = []
  const backtrack = (first = 0) => {
    if (first === nums.length) {
      res.push([...nums])
      return
    }
    for (let i = first; i < nums.length; i++) {
      [nums[first], nums[i]] = [nums[i], nums[first]]
      backtrack(first + 1);
      [nums[first], nums[i]] = [nums[i], nums[first]]
      //                     ^
      // TypeError: Cannot set properties of undefined (setting '3')
    }
  }
  backtrack()
  return res
};

脑子过了一遍好像,for中没有能取到3的时候,于是打log看看在什么情况下i的取值会到3,于是便有下面不同的报错

js 复制代码
var permute = function (nums) {
  const res = []
  const backtrack = (first = 0) => {
    if (first === nums.length) {
      res.push([...nums])
      return
    }
    console.log('---')
    for (let i = first; i < nums.length; i++) {
      console.log(first, i)
      [nums[first], nums[i]] = [nums[i], nums[first]]
      //                     ^
      // TypeError: Cannot set properties of undefined (setting '1')
      backtrack(first + 1)
      [nums[first], nums[i]] = [nums[i], nums[first]]
    }
  }
  backtrack()
  return res
};

先说解决方法:给backtrack()加上引号问题

分析原因:

假设有const arr = ['a', 'b', 'c'],有2个前置知识点要先了解

  1. 当你在数组后面的方括号[中使用逗号分隔的多个索引时,它实际上会返回最后一个索引对应的元素。 arr[1,2] => arr[2] => 'c'
  2. 解构赋值的语法将数组中的值交换位置时,整个表达式返回交换之后的结果 [arr[1], arr[2]] = [arr[2], arr[1]] => ['c', 'b']

因此第一个错误中,有如下执行过程

scss 复制代码
backtrack(first + 1)
[nums[first], nums[i]] = [nums[i], nums[first]]

// 等价于下面的代码
backtrack(first + 1)[nums[first], nums[i]] = [nums[i], nums[first]]

// 当first = 2,执行代码变成这样
backtrack(3)[3,3] 
=> backtrack(3)[3] 
=> undefined[3]

因此便有了,TypeError: Cannot set properties of undefined (setting '3')

结论

JavaScript引擎会使用一个叫做自动分号插入(Automatic Semicolon Insertion,ASI)的机制,在代码解析时在某些地方自动插入分号。 JavaScript引擎试图在这两行代码之间插入一个分号,但是由于数组解构赋值的语法,它误解了这两行代码,导致了TypeError。

参考资料:

  • JavaScript 不会将每个换行符都视为分号:通常仅当无法解析没有分号的代码时,它才会将换行符视为分号。 因此以 "("、"["、"/"、"+"、"-" 开始的语句,那么它极有可能和前一条语句合在一起解释。 《JavaScript:权威指南》第 6 版(David Flanagan,O'Reilly,2011)中收集到的内容:

  • 尤雨溪在JavaScript语句后应该加分号么?中的回答: 至于说 "很难总结什么时候加不加",其实真的很简单。 真正会导致上下行解析出问题的 token 有 5 个:括号,方括号,正则开头的斜杠,加号,减号。 我还从没见过实际代码中用正则、加号、减号作为行首的情况, 所以总结下来就是一句话:一行开头是括号或者方括号的时候加上分号就可以了,其他时候全部不需要。其实即使是这两种情况,在实际代码中也颇为少见。

相关推荐
前端一小卒2 小时前
一个看似“送分”的需求为何翻车?——前端状态机实战指南
前端·javascript·面试
syt_10132 小时前
Object.defineProperty和Proxy实现拦截的区别
开发语言·前端·javascript
长安牧笛2 小时前
儿童屏幕时间管控学习引导系统,核心功能,绑定设备,设时长与时段,识别娱乐,APP超时锁屏,推荐益智内容,生成使用报告,学习达标解锁娱乐
javascript
栀秋6662 小时前
深入浅出链表操作:从Dummy节点到快慢指针的实战精要
前端·javascript·算法
青青很轻_3 小时前
Vue自定义拖拽指令架构解析:从零到一实现元素自由拖拽
前端·javascript·vue.js
xhxxx3 小时前
从被追问到被点赞:我靠“哨兵+快慢指针”展示了面试官真正想看的代码思维
javascript·算法·面试
树下水月3 小时前
纯HTML 调用摄像头 获取拍照后的图片的base64
前端·javascript·html
蜗牛攻城狮3 小时前
Vue 中 `scoped` 样式的实现原理详解
前端·javascript·vue.js
豆苗学前端3 小时前
前端工程化终极指南(Webpack + Gulp + Vite + 实战项目)
前端·javascript
比老马还六3 小时前
Bipes项目二次开发/海龟编程(六)
前端·javascript