作用域

情况一

一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。

var x = 1

function f(x, y = x) {
  console.log(y)
}

f(2) // 2

上面的代码中,参数 y 的默认值等于变量 x。调用参数 f 时,参数形成一个单独的作用域。在这个作用域里面,默认值变量 x 指向第一个参数 x ,而不是全局变量 x ,所以输出是 2

情况二

如果设置了参数的默认值为变量,并且在形成的单独作用域中不存在此变量,那么此变量会指向外层对应的变量

let x = 1

function f(y = x) {
  let x = 2
  console.log(y)
}

f() // 1

上述代码中,函数 f 调用时,参数 y = x 形成一个单独的作用域。这个作用域里面,变量 x 本身没有定义,所以指向外层的全局变量 x。函数调用时,函数体内部的局部变量 x 影响不到默认值变量 x

情况三

如果设置了参数的默认值为变量,在形成的单独作用域中不存在此变量,并且不存在对应的外层变量,就会报错

function f(y = x) {
  let x = 2
  console.log(y)
}

f() // ReferenceError: x is not defined

情况四

var x = 1
function foo(x = x) {
  // ...
}

foo() // ReferenceError: x is not defined

上面代码中,参数 x = x 形成一个单独作用域。实际执行的是 let x = x ,由于暂时性死区的原因,这行代码会报错“ x 未定义”

情况五

如果参数的默认值是一个函数,该函数的作用域也遵守上述规则

let foo = 'outer'

function bar(func = () => foo) {
  let foo = 'inner'
  console.log(func)
}

bar() // outer

上述代码中,函数 bar 的参数 func 是一个匿名函数,返回值为变量 foo ,由于函数参数的单独作用域中不存在 foo 变量,所以 foo 指向全局变量 foo ,因此 func 输出 outer

如果写成下面这种情况,则会报错

function bar(func = () => foo) {
  let foo = 'inner'
  console.log(func())
}

bar() // ReferenceError: foo is not defined

上面代码中,匿名函数里面的 foo 指向函数外层,但是函数外层并没有声明变量 foo,所以就报错了

情况六

var x = 1
function foo(x, y = function() { x = 2 }) {
  var x = 3
  y()
  console.log(x)
}

foo() // 3
x // 1

上面代码中,函数 foo 的参数形成一个单独作用域。这个作用域里面,首先声明了变量 x,然后声明了变量 x ,然后声明了变量 yy 的默认值是一个匿名函数。这个匿名函数内部变量 x,指向同一个作用域的第一个参数 x。函数 foo 内部又声明了一个内部变量 x ,该变量与第一个参数 x 由于不是同一个作用域,所以不是同一个变量,因此执行 y 后,内部变量 x 和外部全局变量 x 的值都没变

如果将 var x = 3var 去除,函数 foo 的内部变量 x 就指向第一个参数 x ,与匿名函数中的 x 指向一致,最终会输出 2

var x = 1;
function foo(x, y = function() { x = 2; }) {
  x = 3;
  y();
  console.log(x);
}

foo() // 2
x // 1

Last updated