轉載一位大神關於變量名與函數名重名問題:閉包
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