(function(x, f = () => x) {
var x = 2;
console.log(f());
})(1)
似乎是最简单的代码,从逻辑上讲,它应该返回2。但它返回1。
我相信如果将这种行为f声明为:
- IIFE型
f = (() => x)() - 简单的视图分配
f = x
在这些情况下,该值f将被计算一次并且已经是常数(很明显,那时f不可能将其作为函数调用,但现在不是这样)。
但是,由于f是一个我认为应该根据 的值以不同方式执行的函数,x所以代码的行为对我来说并不清楚。
我想当被调用时,该函数f将返回 value x,此时它已经等于2(被上面的行覆盖)。但该函数仍然返回1。为什么?
经过反复试验,发现如果f在函数体中重写,
(function(x, f = () => x) {
f = () => x;
var x = 2;
console.log(f());
})(1)
然后代码开始按我的预期工作。由此我得出结论,这里的决定性因素是定义函数的位置(即,它被定义为参数的默认值)。但是,再一次,为什么这很重要?
在写这个问题时,我更进一步,还发现x. 如果代码看起来像这样(没有关键字var),
(function(x, f = () => x) {
x = 2;
console.log(f());
})(1)
那么结论又是合乎逻辑的 - 2。
同时,我假设的第一件事是,由于函数是在参数字符串中定义的(作为默认值),所以它的值x不是作为函数体x中变量的值,而是作为值。但是无论是在场还是在场,价值本身都不会改变。这是函数执行的结果——是的。所以这是另一回事。唯一的问题是什么?argument[0]vararguments[0]
为什么第一个示例的输出
1而不是输出的问题的答案2在于函数的形式参数。规范的当前修订版(版本)在第9.2.12 节 FunctionDeclarationInstantiation中描述了函数的执行(更准确地说,初始化)。它还以纯文本形式表示,在函数初始化期间词法环境及其条目发生的情况取决于形式参数:
特别是,如果函数的形参中至少有一个参数定义了默认值(简称为“默认参数”),则为函数体内的所有声明创建一个单独的词法环境。
正是出于这个原因,表达式在专门创建的词法环境中
var x = 2定义了一个新变量。但是,由于函数定义
f引用了另一个 Lexical Environment(为形参创建的那个),所以当它被调用时,将取变量的值,x记录在其中的 Lexical Environment 的记录中该功能已定义f。简单地说,1.此外,规范将其定位为“功能”。在算法 9.2.12 的步骤27.a中,她说为函数体内的声明创建了一个单独的词法环境,以便形式参数之间的闭包无法访问函数体内声明的变量:
这正是问题中给出的示例中发生的情况。正是从这一点来看,规范试图保护我们(尽管这种“关心”的好处颇有争议,但现在不是这样了)。
当函数的形参中没有一个默认参数时,情况完全相反:
在这种情况下,函数体中的声明和形参之间的声明都指向同一个词法环境。因此,表达式
var x = 2准确地重新定义了存在于同一范围内的变量x。但是,如果
x函数体内的变量是使用关键字声明的let,那么在这种情况下,我们会得到一个语法错误:这是由于关键字的机制
let,特别是不允许重复声明这样的变量。实际上,这是一个简化的解释。在某些情况下,即使使用关键字
let(即使形式参数中没有默认参数),仍然会为此类声明创建一个新的词法环境。附录 B.3.3.1 中有关此内容的更多信息。但是,在这个答案的范围内,这些功能并不是特别有趣,所以我将省略它们。最后一件事,关于问题的最后一个例子:
在这种情况下,答案
2是由于缺少关键字var。由于
x这里的变量不是在新的词法环境中声明的(为什么是新的,上面读过),而是覆盖了x在父词法环境中声明的变量(顺便说一下,对创建的词法环境的外部词法环境的引用)函数体内的声明准确地指向在创建“附加”词法环境之前就存在的词法环境,参见第 27.b) 条,函数调用f将返回已经重新定义的变量值x。