ES6 中的函数(非箭头函数)跟 ES5 的函数是有本质区别的,核心在于作用域,先看一道题:
1 | let x = 0 |
应该打印出什么呢?大家不妨亲自运行一下,看看跟自己想的是否一致。这里总结了两点 ES6 函数中比较特殊的地方:
ES6 函数可能存在两个作用域
在 ES5 中,函数只有一个作用域,但是在 ES6 中,如果给函数参数设置了默认值,那么就会创建两个作用域:
- 函数参数作用域
- 函数体作用域
请看示例:
1 | var x = 1 |
在这一题中,符合函数参数设置了默认值的条件,于是就存在了三个作用域:
- 全局作用域
- 函数参数作用域
- 函数体作用域
在每个作用域下变量的值如下:
1 | -> {x: 3} // 函数体作用域 |
当 y 函数执行的时候,它虽然属于函数体作用域,却是在函数参数作用域下定义的,按照 JS 词法作用域的定义,会先在当前作用域下查找变量,如果找不到再去上级作用域中查找,所以 x = 2 只会影响函数参数 x ,不会影响到函数体内的 x 也不会影响到全局的 x。
出现两个作用域的时候,有一点需要注意:在函数体作用域内,如果出现与函数参数同名的变量,其变量提升的初始值与同名的函数参数相同而不是 undefined。例如:
1 | var a = 1 |
如果把 var 改成 let/const,则会报错:
1 | var a = 1 |
如果同名变量是函数:
1 | var a = 1 |
因为函数声明在变量提升的时候会赋值为函数的堆内存地址。
但是下面的代码就有意思了:
1 | var a = 1 |
不同 JS 引擎下的结果是不一致的,在 Chrome 下打印 1,在 Firefox 下打印 function a,大家可以试试看,希望有大佬可以解释下原因。
ES6 函数参数可能存在暂时性死区
暂时性死区这个概念是 ES6 中提出来的,大家对其印象和应用场景可能仅限于下面的代码:
1 | function f() { |
但是在函数参数中也可能会出现这种问题,也就是开头的那段代码:
1 | let x = 0 // 全局作用域下的 x |
函数参数作用域下也存在类似于 let/const 这种变量提升的机制,即 y 和 x 两个变量被收集起来了,属于 uninitialized 的状态,然后再按顺序赋值,如果没有默认值就赋 undefined,如果有默认值就赋默认值,而如果在变量 initialized 之前被其他变量用到就会报错,这就解释了为什么上面的代码会报错了。