在使用js函数的过程中,有时可能会遇到函数的形参和函数体内局部声明的变量相同的情况,自己也可能无意间声明了同名的局部变量。变量同名时js到底是怎么处理的呢,下面探寻下背后的原理。
ES5中变量声明
有关变量声明的问题,在ES5中有如下代码:
console.log(value); // undefined
var value = 10;
var value;
console.log(value); // 10
可能大家看到上述代码会认为第一行会报错,因为输出value的时候value并没有定义,会抛出一个ReferenceError错误,其实不然。
在js代码的执行时候不完全是一行一行的执行,在有var声明变量的时候,会在编译阶段把var声明的变量提升,把变量赋值保留在原来的位置,上述代码会首先被转成如下代码:
var value;
var value;
console.log(value); // undefined
value = 10;
console.log(value); // 10
变量声明提升之后,在真正的执行的时候如果遇到变量已经声明之后,再次重复声明会被忽略,第二个var value不起作用。所以代码运行之后第一个console.log会输出undefined,第二个console.log会输出10。
ES5中函数形参同名变量
在ES5中允许使用var声明形参同名变量,函数用var声明的变量同样遵循变量提升原则
function func(value) {
console.log(value); // 10
var value;
console.log(value); // 10
}
func(10)
上述代码中由于变量已经在形参中声明,并且执行的时候已经赋值10,所以两个console.log都会输出10。
ES6中函数形参同名变量
在ES6中,如果函数中出现与形参同名的变量有两种情况,形参与ES5定义一样的情况和形参有默认值、rest参数等的情况。如果形参局部变量定义与ES5的方式相同,那么执行结果与上面讨论的并无不同。以下详细介绍下ES6中函数含有默认值形参的情况,有如下代码:
function func(value = 10) {
var value = 20;
console.log(value); // 20
}
func();
;
上面的实例大家可能一眼就看出console.log会输出20,会认为形参的value和局部变量的value是同一个变量。其实事情没有那么简单,形参的value和局部参数的value并不是同一个变量,它们分别在不在一个作用域,形参定义为一个作用域,局部变量的定义属于另外一个全新的块级作用域,可以从chrome浏览器debug模式下查看不同的作用域(Scope)
看了以上的例子就很容易看出来以下代码执行console.log会输出20而不是10了
function func(
value = 0,
setValue = () => { value = 10 }
) {
var value = 20;
setValue();
console.log(value); // 20
}
func();
为什么含有默认值的形参和同名的var变量会属于不同的作用域?
因为在ES5中形参和同名var变量是同一个值,在ES6中这种写法仍然正确,但是含有默认值的形参是用let定义,如果使用var定义的同名变量仍属同一个作用域会导致SyntaxError错误(变量重复声明),为了解决这个问题设置了不同的作用域。
通过以上剖析,建议尽量不要在函数体中用var声明形参同名变量,这样会导致一些意想不到的问题,会给程序的正常运行埋下隐患。