全局变量名、局部变量名和形参名冲突时,覆盖情况

以前错误的认为,全局变量名、局部变量名和形参名相同时,全局变量(也就是外部变量)被形参覆盖,形参被局部变量覆盖。

今天发现这样理解并不对。比如

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){
}

 

posted @ 2016-09-27 00:36  liyan.web  阅读(5722)  评论(1编辑  收藏  举报