js中變數名與函式名重名的問題

2021-09-26 09:49:56 字數 2771 閱讀 3786

網上有很多的部落格有談到這個重名的問題,但是我覺得講不算是很全面,至少我看了還是不懂下面這道題到底是怎麼回事。那在講解這個問題的時候,我們先來看看下面這道題,一道面試題(南山西麗xx研究院):

var a = 1;

function b()

}b();

console.log(a);

這題列印出a的值為多少呢?可能會有很多的同學認為列印出的值為10,但其實並不是,為什麼呢?

誤區1:變數提公升

我想大家都明白,在執行函式b的時候,由於其內部有一句a=10,前面並沒有var,所以在執行完函式b之後,認為變數a提公升為全域性變數,並且10這個值覆蓋了之前的1。所以列印出的值為10。我想的話這是多數新手的思路。

這裡有個知識點:

顯示宣告:var a = 1;

隱式宣告:a = 1;

隱式宣告會變數提公升為全域性變數

誤區2:不知道函式宣告也是有提公升的

多數新手認為在執行函式b的時候,js執行**自上往下,執行到return的時候退出函式,不再執行函式a,所以認為這個函式a根本不起作用。

js知識盲區:js預解析與解析變數宣告的順序

js預解析:在執行**之前,js會預解析**,**中如果有變數宣告和函式宣告的話,那麼便將變數宣告和函式宣告置頂,網上多數人認為函式宣告置頂比變數宣告置頂更優先。(事實上是如此嗎?這裡先埋乙個坑,稍後再來討論)

js解析宣告變數的順序:js在解析變數宣告的時候,是分為兩個步驟的。

比如這句**:var a = 1;其實是分為(1)var a;(2)a=1;兩個解析步驟來進行的。

好,講完兩個知識點,那麼我將原**改寫成如下形式:

function b()

a = 10;

return;

}var a;

a = 1;

b();

console.log(a);

好,懂作用域鏈的同學應該明白,在呼叫變數或者乙個物件屬性等的時候,查詢的順序是由裡向外的,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()

var a;

a = 1;

b();

console.log(a);

答案很明顯是10,為什麼?大多數同學都知道的吧,是的,這裡在函式b裡的作用域裡沒有查詢到名為a的物件或者變數,那麼繼續向外查詢,發現在全域性作用域裡發現了有變數a的記憶體位址,於是將10這個值賦值給了全域性變數a。

懂了?好,我們再將**再修改一次,如下:

function b()

b();

console.log(a);

這個很簡單的吧?列印的值為多少呢?依舊是10對吧,為什麼呢?是這樣的,在執行函式b的時候,發現有乙個隱式宣告a=10,它在函式b裡的作用域中查詢不到有關於名為a的位址,於是向外查詢,發現全域性作用域下也沒有,那麼變數提公升,系統預設給變數a提供乙個記憶體,並將值賦值給變數a,所以變數a變數提公升為全域性變數了。所以列印出的值為10.

**1:

var a;

function a()

console.log(a);

**2:

function a()  

var a;

console.log(a);

執行之後我們會發現,兩段**執行的結果是一樣的,均為:function a()。

這就應證了網上的說法:函式宣告置頂比變數宣告置頂更優先。如果不是的話,那麼第二段**應該列印undefined,可是為什麼列印出的卻是函式a呢?這個例子是否告訴我們變數宣告置頂比函式宣告置頂更優先(這個是我同學研究出來的)?但是總歸一句話,變數賦值肯定是在兩個宣告的下面的,所以這並不妨礙我們編寫**,或者做面試題。當然如果剛好出了這麼一道題,我希望大家把它當特殊例子對待吧。

變數名與函式名重名的總結:

1.要知道js解析變數宣告的順序

2.函式宣告和變數宣告會置頂且函式宣告更優先的說法不對!應該是變數宣告比函式宣告更優先!

3.作用域鏈的查詢順序是由裡向外,js執行**順序是自上往下

JS中變數名和函式名重名

var x 12 var x 13 function x console.log x 13 err x is not a function x 這個串 執行完會報錯 a is not a function 問題來了,為什麼會報這個錯誤呢?這裡涉及到函式和變數的預解析 1 函式宣告會置頂 2 變數宣告...

JS中變數名和函式名重名

var x 12 var x 13 function x console.log x 13 err x is not a function x 這個串 執行完會報錯 a is not a function 問題來了,為什麼會報這個錯誤呢?這裡涉及到函式和變數的預解析 1 函式宣告會置頂 2 變數宣告...

JS中變數名和函式名重名

var a 100 function a a a is not a function 原來函式宣告會置頂 變數宣告也會置頂 函式宣告比變數宣告更置頂 牢記 函式是一等公民 變數和複製語句一起書寫,在js引擎解析時,會將其拆成宣告和賦值兩個部分,宣告置頂,賦值保留在原來的位置 宣告或的變數不會重複宣告...