以前错误的认为,全局变量名、局部变量名和形参名相同时,全局变量(也就是外部变量)被形参覆盖,形参被局部变量覆盖。
今天发现这样理解并不对。比如
function foo(num){ var num; console.log(num); } foo(1) // 1 //如果错误的理解为局部变量会覆盖形参的话,会认为会输出undefined
那事实是什么呢。实际上变量名冲突分两种,一种是函数外的变量和函数里的局部变量的冲突,一种是函数内部的冲突。
第一种冲突,我把它理解为是作用域链的上游(最上游是全局对象的命名空间)会被下游函数的局部变量覆盖。
其实我觉得这就是一种继承关系,好比原型链里离目标对象近的新的方法覆盖远的旧的方法,甚至是好比HTML/CSS里字体、颜色等属性的继承。
只不过这里继承的是一个上下文环境。
它们都有一个特点,自己有,用自己的;自己没有,用父级的;父级没有,再逐级向上。
//和HTML/CSS属性继承不同的是,JS作用域链还涉及变量/函数声明 //简单说,就是子元素一旦声明了变量,不管有没有赋值,都算做是有自己的了 //也就不会操作到父级了 var b; function foo2() { console.log(b); var b; } foo2();//undefined
第二中是函数内部的冲突,即同一作用域内的冲突。
形参和局部变量其实就是同一作用域的。只不过JS里的形参省略了声明(像是Java就需要和变量一样做类型声明)。
而在JS里有:同一作用域,重复申明但不赋值,不会对变量有影响
var a=1; console.log(a); //1 var a; console.log(a); //1
这也就解释了,为什么局部变量申明但不赋值,不影响形参。
或许你会有我之前的另一种猜测,局部变量声明但不赋值,不会覆盖形参,但赋值了不是改变形参,而是覆盖。
那我们再看看这个
var b; function foo2(b) { var b=2; console.log(b); console.log(arguments[0]); } foo2(1); //假如是局部变量名覆盖形参名的话,那应该打印'2 1' //而事实是'2 2' //所以其实局部变量和形参冲突,就是重复定义个变量 //所以我觉得其实可以把形参理解为一个特殊的局部变量 //只不过它在函数定义时被申明,而且申明的var 被省略 function foo2(var b){ }