为啥链接到 Promise.resolve() 的 .then() 允许重新分配 const 声明?

Posted

技术标签:

【中文标题】为啥链接到 Promise.resolve() 的 .then() 允许重新分配 const 声明?【英文标题】:Why does .then() chained to Promise.resolve() allow const declaration to be reassigned?为什么链接到 Promise.resolve() 的 .then() 允许重新分配 const 声明? 【发布时间】:2018-01-04 22:40:57 【问题描述】:

鉴于使用const 声明的变量无法重新分配或删除,请参阅

Are there constants in javascript?

JavaScript const Keyword

Is it possible to delete a variable declared using const??

为什么可以在传递给.then() 的函数中为使用const 声明的变量重新分配值,该函数链接到Promise.resolve(),其中传递了const 变量,但无法重新分配@987654329 @ 函数传递给.then() 的变量链接到Promise 构造函数,其中const 变量传递给resolve() 参数Promise 构造函数resolver 函数?

"use strict"
const state = "123";
Promise.resolve(state)
.then(state => 
  console.log(state); // `"123"`
  state = 456; // reassign `const` variable `state` to `"456"`
  return state
)
.then(state => console.log(state)) // `"456"`
// not reached
.catch(err => console.error(err.message));


  "use strict";
  const state = "123";
  new Promise(resolve => 
    console.log(state); // `"123"`
    state = "456"; // reassign `const` variable `state` to `456`
    resolve(state);
  )
  .then(state => 
    console.log(state);
  )
  // `Error: Assignment to constant variable.`
  .catch(err => console.error(err.message)); 

编辑,更新

为了阐明调查的基础和动机,该问题试图确定何时可以使用与 const 声明相同的标识符,以及何时无法使用该程序。本质上,尝试创建一个标识符,该标识符会在尝试为标识符分配不同值的任何范围内引发错误——无论是函数范围、块范围还是代码中的任何位置——“superconst”标识符,或者最接近当前可能的描述,具体取决于引擎。 MapWeakMapclass 是我们目前在最新浏览器实现中最接近的吗?

【问题讨论】:

@loganfsmyth 函数参数在函数范围内是可变的还是可变的const 变量? const 声明作为参数传递的事实是否允许重新分配原始值? 您创建了一个名为state 的新变量作为.then() 处理程序的参数,这就是您要分配的变量,并且该新变量不是const。它“遮蔽”或“覆盖”在更高范围内声明的同名const 变量。将函数参数的名称从state 更改为localState,您将看到分配给state 并没有按照您的想法执行。 正如我在回答末尾所说的那样,据我所知,Javascript 没有阻止较低范围定义自己的变量的功能,该变量与较高范围的 @987654352 同名@ 变量,因此“隐藏”了更高范围的变量。这不是该语言的特征。我不理解您对MapWeakMapClass 的引用与此讨论或superconst 的含义有关。 @jfriend00 WeakMap 密钥只能是原始密钥,是吗?或者WeakMap 实现是否有例外情况?您的答案中的文本阐明了函数参数处的标识符被排除在引发错误之外。这个概念是一个标识符的可能性,它不能引用除原始且唯一的赋值之外的任何其他值。实际上并没有期望任何结果,特别是对于问题的代码。该代码是尝试查找或创建满足上述要求的模式的尝试,或者确定该要求当前是否不可能。 符号可以复制到多个变量就好了。 let a = Symbol(); let b = a; 然后,您可以使用ab 访问Map 中的相同元素。我很高兴看到您接受了答案,因为这变成了一个漫长而曲折的讨论,而不是堆栈溢出主题的问答类型。您可能想问一个新问题,它解释了您真正想要解决的问题,尽管我怀疑这不是 Javascript 支持的东西。 【参考方案1】:

您没有分配给const 变量。相反,您正在分配给您提供相同名称的函数参数。该函数参数是变量的非常量副本,因此您可以分配给它。

我将尝试将我所有的 cmets 收集到一个更充分解释的答案中。

这里的代码:

"use strict"
const state = "123";
Promise.resolve(state).then(state => 
  console.log(state); // `"123"`
  state = 456; // reassign `const` variable `state` to `"456"`
  return state
).then(state => console.log(state)) // `"456"`
  // not reached
.catch(err => console.error(err.message));

首先,定义const state = "123" 变量。任何更改该确切 state 变量内容的尝试都将引发异常。

然后,当你这样做时:

Promise.resolve(state).then(state => 

这声明了一个.then() 处理函数,它接受一个参数并且该参数的名称是state。当调用.then() 处理程序时,作为该参数传递给.then() 处理程序的任何内容都将复制到这个名为state 的新参数变量中。函数参数不是const。他们可以被分配到。

因为您现在已经创建了两个具有相同名称的单独变量并且一个位于更高范围内,所以当您在 .then() 处理程序中时,名为 state 的函数参数“覆盖”或“隐藏”另一个同名变量。当您尝试在 .then() 处理程序中访问 state 时,使用该名称时您可以访问的唯一变量是函数参数。该函数参数是另一个状态变量的副本,因为它作为参数传递给.then() 处理程序。所有函数参数都是副本。 Javascript 没有真正的引用变量类型。

此外,函数参数不是const,因此您可以分配给它们。

因此,当您在 .then() 处理程序中使用 state = "456"; 时,您只是分配给函数参数。因为您已经创建了命名冲突,所以实际上无法访问更高范围的 const state 变量。 JS 解释器会找到在范围内最接近您尝试访问它的定义。


我认为,如果您停止创建有冲突的变量名,您的困惑就会迎刃而解。如果你这样做(命名参数localState):

"use strict"
const state = "123";
Promise.resolve(state).then(localState => 
  console.log(state); // `"123"`
  state = 456; // reassign `const` variable `state` to `"456"`
  return state
).then(state => console.log(state)) // `"456"`
  // not reached
.catch(err => console.error(err.message));

然后,当您尝试分配给state 时,您将看到一个异常,因为您没有创建具有相同名称的冲突局部变量,因此您尝试分配state = 456 确实会尝试分配给@987654345 @变量,解释器会反对。


据我所知,Javascript 无法阻止在本地范围内使用新声明的同名变量覆盖更高范围的变量。那不是语言功能。当解释器解析变量名时,它会搜索从局部到全局的范围层次结构,因此首先找到(并使用)局部定义。更高范围的定义在该范围内被“覆盖”或“隐藏”。这就是他们设计变量名称解析以在该语言中工作的方式。

这样做也有很多好处,因为有人突然声明了一个您没有使用或什至不知道的更高范围的变量,这永远不会意外地破坏您的更低范围的声明。当您自己声明冲突并且您实际上想要使用更高范围的命名时,这只是一个编码错误。如果您打算使用同名的更高范围的变量,则不必声明冲突的名称。

【讨论】:

【参考方案2】:

这里的常量state 变量没有改变。您正在更改传递给 resolve 函数的参数值。您可以通过简单地执行console.log(state) 来验证这一点,因为您的所有代码和输出都是123

【讨论】:

函数参数在函数作用域内是可变的还是mutatedconst变量? 这是功能块中发生的事情: var state = state //(这个状态是你的 const 变量) const 变量没有发生变异。您只是在使用它的值并在功能块中定义具有相同名称的新变量。 为什么使用Promise构造函数的模式会抛出错误? @guest271314 - 函数参数就像一个局部变量(作用于函数)。它不是const。无论您在其中传递了什么,它的行为都是相同的,因此您传递const 值并不重要。只有原始变量是const。这是一个新变量,不是const @guest271314 - 我将评论移至答案中。

以上是关于为啥链接到 Promise.resolve() 的 .then() 允许重新分配 const 声明?的主要内容,如果未能解决你的问题,请参考以下文章

Promise.resolve 与新的 Promise(resolve)

Promise.resolve().then vs setImmediate vs nextTick

Promise.resolve()的参数分4种情况

.resolve() 和 .promise() 有啥区别?

Promise.resolve()与new Promise(r =; r(v))

Promise.resolve 是异步函数吗?