【问题标题】:Are function parameters in JavaScript hoisted?JavaScript 中的函数参数是否被提升?
【发布时间】:2020-05-09 13:49:48
【问题描述】:

function foo(a,b){
  return a + b;
}

foo(1,2);

函数参数是否被提升?

在函数执行上下文的创建阶段,variableEnvirnoment 是否看起来像这样:

VE = {
        { 0 : undefined , 1: undefined, length: 2 },
        {a : undefined, b: undefined},
        outer: refToGlobalLE
     }

【问题讨论】:

  • 在这种情况下,“提升”是什么意思?
  • 是的,在函数 foo 上下文中。
  • 这并没有真正澄清任何事情。
  • 是什么意思?在这里将任何东西称为被提升是没有意义的。 函数本身会被提升,所以如果你将调用移到定义之上,它仍然可以工作。
  • 最早可以引用函数参数的点是函数体的开头。所以说提升对于参数没有任何意义。

标签: javascript hoisting executioncontext


【解决方案1】:

是的,参数被提升了。

调用函数时,函数体中的每个声明(varletconst 和函数声明)在该执行上下文的环境记录中实例化。每个形式参数也以相同的方式添加到环境记录中([9.2.10 21.c.i 和 9.2.10 28.e.i.2])。整个过程在规范的section 9.2.10 中进行了描述。

形式参数和函数声明都在称为FunctionDeclarationInstantiation 的抽象操作期间初始化(注意:不是“实例化”)。所有其他绑定都在函数体求值期间初始化。

这意味着形式参数绑定以与函数体声明相同的方式添加到相同的逻辑位置,即。 varletconst 和函数声明(顺便说一句,all of which are hoisted)。这意味着形式参数绑定的初始化与函数声明的方式相同(即它们的对应值设置在函数的顶部)。

提升是一个标识符绑定实例化的时间和地点的函数。形式参数绑定以与函数声明相同的方式实例化(和初始化)。我们知道函数声明被提升,因此参数绑定被提升。 QED。

据我所知,在 ES2015 之前,这种提升是不可见的,因为函数参数已经在函数的最顶部。但是,在 ES2015 中添加了参数默认值初始化器语法,使得提升在用户空间中可见。

如果形式参数没有被提升,那么下面的代码不会抛出"Uncaught ReferenceError: Cannot access 'x' before initialization",因为z的默认参数值将引用外部x

var x = 'global'
(function(y, z = x, x) {}()) // "Uncaught ReferenceError: Cannot access 'x' before initialization"

您的评论说'假设参数被提升,x 将 [被初始化为]“未定义”,因此不会发生错误,因为 x 已经初始化 '。你的意思是提升也可以用来解释错误的缺席。这是真的,但因此并不意味着如果观察到错误,就不会发生提升。实际上,我们可以看到提升正在发生,因为如错误消息中所述,z = x 中的x 被认为是指形式参数x,该参数稍后在程序文本中声明。唯一可能的方法是提升已经发生。

【讨论】:

  • 感谢您的回答,但据我所知。提升是在变量环境中将变量声明(仅 var)设置为未定义并将函数声明作为字符串的过程。那么,词法环境将只是一开始的变量环境的副本。因此,假设参数被提升,x 将得到“未定义”,因此错误不会发生,因为 x 已经初始化。但是,我觉得参数是一种特殊情况,它们在创建阶段使用提供的参数进行初始化。我们仍然称其为提升吗?
  • 我已经参考规范更新了我的答案。
【解决方案2】:

不,函数参数没有在 JavaScript 中提升。

function foo(a,b){
  return a + b;
}

foo(1,2);

为了简单地解释整个过程,我想解释一下函数本身是如何被调用的。 因此,由于您在谈论变量环境(在这种情况下是全局执行上下文),因此基本上逐行执行代码的 JavaScript 执行线程会将函数定义代码作为值存储到 foo 变量中内存并且它还没有执行它(它只是按原样保存定义)。它移动到下一个执行行,该行显然对该 foo 函数定义进行了函数调用(使用括号)。

现在,当调用该函数 foo(1,2) 时,首先会在该 foo 函数的全局执行中创建一个新的执行上下文。你可以把它想象成一个抽象的盒子。参数 1 和 2 与函数的“a”和“b”参数映射为 foo 执行上下文中的变量,而不是全局执行上下文。然后,函数只是通过调用栈将a+b的值返回给全局执行上下文。

好的,所以要检查一下,如果您只是调用该函数,然后像下面那样定义它

foo(1,2);

function foo(a,b){
  return a + b;
}

您可能会收到错误,因为 foo 函数定义不存在于全局执行上下文中,并且基本上会导致 REFERENCE ERROR。

现在,谈谈参数本身,无论它们是否被提升。那就不要!当函数被调用并创建新的执行上下文时,它们本身就会创建。这些变量是块作用域的函数,对外部/全局执行上下文不可见。因此,当函数返回给被调用者时,其中的变量只是被垃圾回收。

希望能回答你的问题。

【讨论】:

  • 感谢您的回答。您的最后一段澄清了这一点并消除了混淆。
  • 我认为这个答案是不正确的。请看我的回答。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-18
  • 2021-04-26
  • 1970-01-01
相关资源
最近更新 更多