TDZ在函数参数的未声明变量中

时间:2018-12-14 11:30:39

标签: javascript function

当我尝试运行此代码段中定义的foo函数时,自ReferenceError起我得到了b is not defined

var b = 3;

function foo( a = 42, b = a + b + 5 ) {
    // ..
}
foo()

这看起来像是TDZ错误,因为b已在外部范围中定义,但尚未在函数签名中用作右侧值。

这是我认为编译器应该做的:

var b;
function foo(..) { .. }

// hoist all functions and variables declarations to the top

// then perform assignments operations

b = 3;
foo();

//create a new execution environment for `foo`
// add `foo` on top of the callstack

// look for variable a, can't find one, hence automatically create a 
   `var a` in the local execution environment and assign to it the 
    value `42`
// look for a `var b` in the global execution context, find one, use 
   the value in it (`3`) as a right-hand-side value.

这不应引发ReferenceError。看起来这不是这里发生的事情。

有人可以解释编译器的实际作用以及如何处理此代码吗?

2 个答案:

答案 0 :(得分:2)

在每个函数调用中,引擎都会评估一些序言代码,其中包含形式参数,声明为let vars,并使用其实际值或默认表达式(如果提供)初始化:

var b = 3;

function foo( ) {
    let a = <actual param for a> OR 42;
    let b = <actual param for b> OR a + b + 5;
   // ..
}

由于函数中的b是词法(let),因此无法在初始化之前访问其值。因此是ReferenceError。

请注意,这是一个调用时错误,因此可以进行以下编译:

var b = 1

function foo(b=b) {
  console.log(b)
}

当您实际调用该函数时发生错误:

var b = 1

function foo(b=b) {
  console.log(b)
}

foo() 

并且仅当引擎实际评估错误的默认表达式时:

var b = 1

function foo(b=b) {
  console.log(b)
}

foo(7) 

ECMA标准参考:FunctionDeclarationInstantiation,第21页:

  

对于parameterNames中的每个String paramName,请执行

     

...执行! envRec.CreateMutableBinding(paramName,false)。

答案 1 :(得分:2)

函数自变量有点像'let'。

在声明之前,我们无法访问使用'let'创建的变量。即,未提升使用'let'创建的变量。

发生这种情况是因为,如果我们在局部范围内声明变量,则它无法访问全局范围变量(除非使用'this') 您的代码可以由此固定-

var b = 3;

function foo( a = 42, b = a + this.b + 5 ) {
    // default binding. In this case this.b = global var
}
foo()

如果您这样做,也会看到此错误。

let variable = variable;