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

故事背景

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

相关推荐
Rainbow_Pearl11 分钟前
Vue2_element 表头查询功能
javascript·vue.js·elementui
此乃大忽悠1 小时前
XSS(ctfshow)
javascript·web安全·xss·ctfshow
江城开朗的豌豆2 小时前
Vuex数据突然消失?六招教你轻松找回来!
前端·javascript·vue.js
好奇心笔记2 小时前
ai写代码随机拉大的,所以我准备给AI出一个设计规范
前端·javascript
江城开朗的豌豆2 小时前
Vue状态管理进阶:数据到底是怎么"跑"的?
前端·javascript·vue.js
我想说一句2 小时前
React待办事项开发记:Hook魔法与组件间的悄悄话
前端·javascript·前端框架
真夜2 小时前
CommonJS与ESM
前端·javascript
G等你下课2 小时前
从点击到执行:如何优雅地控制高频事件触发频率
前端·javascript·面试
Jackson_Mseven2 小时前
面试官:说说 startTransition 和 useDeferredValue?我:我用它一行代码救了首页!
前端·javascript·面试
架构个驾驾2 小时前
从0到1搭建Vue3+Vant移动端项目(一)
前端·javascript·vue.js