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

故事背景

今天在刷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 个:括号,方括号,正则开头的斜杠,加号,减号。 我还从没见过实际代码中用正则、加号、减号作为行首的情况, 所以总结下来就是一句话:一行开头是括号或者方括号的时候加上分号就可以了,其他时候全部不需要。其实即使是这两种情况,在实际代码中也颇为少见。

相关推荐
@大迁世界17 分钟前
Vue 设计模式 实战指南
前端·javascript·vue.js·设计模式·ecmascript
jump_jump1 小时前
妙用 localeCompare 获取汉字拼音首字母
前端·javascript·浏览器
U.2 SSD1 小时前
Echarts单轴坐标系散点图
前端·javascript·echarts
不做无法实现的梦~1 小时前
jetson刷系统之后没有浏览器--解决办法
开发语言·javascript·ecmascript
Jedi Hongbin2 小时前
Three.js NodeMaterial 节点材质系统文档
前端·javascript·three.js·nodematerial
前端小马2 小时前
前后端Long类型ID精度丢失问题
java·前端·javascript·后端
liu****3 小时前
基于websocket的多用户网页五子棋(八)
服务器·前端·javascript·数据库·c++·websocket·个人开发
San303 小时前
深入理解 JavaScript 函数:从基础到高阶应用
前端·javascript·node.js
芒果茶叶4 小时前
并行SSR,SSR并行加载
前端·javascript·架构
威风的虫5 小时前
JavaScript中的axios
开发语言·javascript·ecmascript