JavaScript中变量名与函数名重名的问题

转载一位大神关于变量名与函数名重名问题:闭包

var a = 1;
function b(){
a = 10;
return;
function a(){
console.log(a);
}
}
b();
console.log(a);
这题打印出a的值为多少呢?可能会有不少的同窗认为打印出的值为10,但其实并非,为何呢?函数

误区1:变量提高.net

我想你们都明白,在执行函数b的时候,因为其内部有一句a=10,前面并无var,因此在执行完函数b以后,认为变量a提高为全局变量,而且10这个值覆盖了以前的1。因此打印出的值为10。我想的话这是多数新手的思路。对象

这里有个知识点:blog

显示声明:var a = 1;内存

隐式声明:a = 1;作用域

隐式声明会变量提高为全局变量io

误区2:不知道函数声明也是有提高的console

多数新手认为在执行函数b的时候,js执行代码自上往下,执行到return的时候退出函数,再也不执行函数a,因此认为这个函数a根本不起做用。function

js知识盲区:js预解析与解析变量声明的顺序

js预解析:在执行代码以前,js会预解析代码,代码中若是有变量声明和函数声明的话,那么便将变量声明和函数声明置顶,网上多数人认为函数声明置顶比变量声明置顶更优先。(事实上是如此吗?这里先埋一个坑,稍后再来讨论)

js解析声明变量的顺序:js在解析变量声明的时候,是分为两个步骤的。

好比这句代码:var a = 1;实际上是分为(1)var a;(2)a=1;两个解析步骤来进行的。

好,讲完两个知识点,那么我将原代码改写成以下形式:

function b(){
function a(){    //函数声明置顶
console.log(a);
}
a = 10;
return;
}
var a;
a = 1;
b();
console.log(a);
那么你们再来看看这段代码,以为大家心目中的答案是多少呢?如果仍是以为等于10的话,那么就说明大家尚未考虑到本文要讲的重点:重名问题!

在讲解这段代码前,我还要给你们要普及一个知识点:做用域链(如果有不懂的同窗,能够查阅连接:js闭包)。

好,懂做用域链的同窗应该明白,在调用变量或者一个对象属性等的时候,查找的顺序是由里向外的,js执行代码是自上往下的。

那么咱们再来分析刚才修改事后的代码:

1)先声明变量a,系统给变量a分配一个内存,注意,这里变量a暂时还不知道它的变量类型,由于尚未赋值;

2)a=1,给变量赋值为1,变量类型为number;

3)开始执行函数b,函数b内有一个函数名为a的函数声明,且函数a下面有一个变量名为a的变量,且赋值为10,这里便开始了难点分析。首先看函数a,因为在执行函数b的时候,并无调用函数a,所以函数a并无起做用;可是执行到a=10的时候,js是这样作的:1.首先查找变量a的地址,从系统内存中开始按照做用域链查找;2.因为做用域链的查找顺序是由里向外的,故要先从函数b里面开始查找;3.在查找的过程当中,发现函数b中已经声明了一个函数名为a的函数(重名问题!),因此查找到函数名为a的函数后,这里便再也不往外查找;4.因此这里的a=10实际上是将10赋值给了函数名为a的这个函数对象!

有的同窗会问为何了。这样说吧,在函数b里面的时候,函数a提高置顶,跟变量相似(都在js中声明了),Js给这个函数a分配了内存,而恰巧的是在执行a=10的时候,优先查找的是与变量a同名的函数a,由于它所在的做用域最近!讲到这里,我想绝大多数的同窗已经明白了这题为何会打印1了吧?

由于既然a=10这个值10赋值给了函数对象a,那么在全局环境下运行console.log(a),访问的是全局变量a,而全局变量a在系统内存中查找的值为1,因此打印的值为1.

那么同窗们能够自行将函数b中的函数a给注释掉,看看打印的结果是什么?看代码:

function b(){
a = 10;
return;
}
var a;
a = 1;
b();
console.log(a);
答案很明显是10,为何?大多数同窗都知道的吧,是的,这里在函数b里的做用域里没有查找到名为a的对象或者变量,那么继续向外查找,发如今全局做用域里发现了有变量a的内存地址,因而将10这个值赋值给了全局变量a。

懂了?好,咱们再将代码再修改一次,以下:

function b(){
a = 10;
return;
}
b();
console.log(a);
这个很简单的吧?打印的值为多少呢?依旧是10对吧,为何呢?是这样的,在执行函数b的时候,发现有一个隐式声明a=10,它在函数b里的做用域中查找不到有关于名为a的地址,因而向外查找,发现全局做用域下也没有,那么变量提高,系统默认给变量a提供一个内存,并将值赋值给变量a,因此变量a变量提高为全局变量了。因此打印出的值为10.

好,讲完这个,咱们继续看看我刚才埋下的坑,如今你们来运行这两段代码:

代码1:

var a;
function a(){  
  console.log(10);  
}  
console.log(a);
代码2:

function a(){  
  console.log(10);  
}  
var a;
console.log(a);
运行以后咱们会发现,两段代码运行的结果是同样的,均为:function a(){console.log(10)}。

这就应证了网上的说法:函数声明置顶比变量声明置顶更优先。若是不是的话,那么第二段代码应该打印undefined,但是为何打印出的倒是函数a呢?这个例子是否告诉咱们变量声明置顶比函数声明置顶更优先?

但很明确的告知你们,函数声明置顶比变量声明置顶更优先。但上述的结果不是按照声明提高的思惟来看,是由于在预解析阶段变量只声明了,但未赋值,而函数不同,它在预解析阶段就已经声明和赋值了,它的值就是函数这个对象,而在打印的时候,打印是须要出具体的值的,而变量还未赋值,函数却已赋值了,因此两次运行结果均是function a(){console.log(10)}。若是去掉函数a,运行的结果是undefined,是由于a被初始化并赋值为undefined了。

变量名与函数名重名的总结:

1.要知道js解析变量声明的顺序

2.函数声明和变量声明会置顶且函数声明更优先!

3.做用域链的查找顺序是由里向外,js执行代码顺序是自上往下————————————————版权声明:本文为CSDN博主「Charles_Tian」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接及本声明。原文连接:https://blog.csdn.net/charles_tian/article/details/79775909