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

故事背景

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

相关推荐
席之郎小果冻3 分钟前
【03】【创建型】【聊一聊,单例模式】
开发语言·javascript·单例模式
前端老曹29 分钟前
vue3 三级路由无法缓存的终极解决方案
前端·javascript·vue.js·vue
ghfdgbg40 分钟前
15. Vue工程化 + ElementPlus
前端·javascript·vue.js
古城小栈1 小时前
JS 中有 undefined 和 null 两个空值,还有谁!
javascript·ecmascript
前端小端长1 小时前
深入理解Composition API与Vue3.0响应式原理
开发语言·javascript
哆啦A梦158810 小时前
商城后台管理系统 03 登录布局
javascript·vue.js·elementui
曼巴UE510 小时前
UE FString, FName ,FText 三者转换,再次学习,官方文档理解
服务器·前端·javascript
selt79111 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript
行走的陀螺仪11 小时前
高级前端 Input 公共组件设计方案(Vue3 + TypeScript)
前端·javascript·typescript·vue·组件设计方案
一颗不甘坠落的流星11 小时前
【Antd】基于 Upload 组件,导入Json文件并转换为Json数据
前端·javascript·json