js中常見的錯誤,例如Uncaught TypeError: x is not a function
其原因除了函數本身有錯之外,還有一種很奇怪的情況:函數本身沒有錯,但是運行時就是不能正常運行。這種情況與javascript的特性有關:變量與函數聲明前置的優先級。
總結:
js有聲明前置,函數和變量的聲明都會前置,即會在整個js代碼的最開始,不管聲明部分在什么位置。
js中函數的聲明優先於同名變量的聲明,不管先后順序如何。
函數的聲明會在整段js代碼的最前面,不管function() 函數在js的什么位置。
當先聲明了名為x()的函數,再聲明名為var x的變量時,變量名會覆蓋函數,使得在同名變量定義之后,函數變得未定義,即同名變量定義之后 調用同名函數會報錯,即 x is not a function。
當有兩個同名變量和兩個同名函數一起定義時,四個量的名字相同,那么后一個函數或者變量會覆蓋掉前一個對應的量(函數/變量)且四個中最后一個定義量之后在引用此量,該量的類型(變量/函數)是最后一次定義此名量時的類型(變量/函數)。
1-4點解釋如下:
首先看代碼:
console.log(x) console.log(x()); var x=1; function x(){ console.log(5); } console.log(x) console.log(x());//此時x變成了一個變量
輸出結果:
function x(){
console.log(5);
}
5
1
Uncaught TypeError: x is not a function
----------------------------
js解釋器在對其上下文進行解釋執行時分為三個階段來進行:聲明階段、初始化階段、執行階段。
針對js上下文,首先會進行聲明階段,聲明階段中的特點是聲明前置;聲明又會包括變量聲明前置和函數聲明前置,鑒於以上代碼的輸出結果,我們可以得出函數聲明前置優先於變量聲明前置的特點,並且如果變量名和函數名沖突會忽略變量的聲明,因此聲明過得變量名或函數名不會重復聲明,這樣也可以很好地解釋為什么第一次輸出的是函數而不是undefined。根據js的這些特點我們可以將以上代碼解析成如下:
//聲明階段 function x(){//函數聲明 console.log(5); } var x;//變量聲明,因為x已經聲明過了,此處不進行聲明(忽略) //執行階段 console.log(x); console.log(x()); x=1; console.log(x); console.log(x());
如上代碼所述,js將變量和函數的聲明前置,然后再執行代碼。
- 第二次輸出時,因為聲明階段已經聲明過名為x的函數,所以在執行階段中調用x函數,會執行函數體中的內容。
- 第三次輸出時,輸出1,因為x被賦值為1.
- 第四次輸出時,因為x此時是一個變量而不是一個函數,所以js無法解釋“變量()”這樣的格式,就會提示“x is not a function”。
第5點解釋如下:當有兩個同名變量和兩個同名函數一起定義時,四個量的名字相同,那么后一個函數或者變量會覆蓋掉前一個對應的量(函數/變量)。
js中聲明過得變量名或函數名不會重復聲明,如果js代碼中有同名的函數或同名的變量時,程序如何運行,如下代碼:
console.log(x) console.log(x()); var x=1; var x=100; function x(){ console.log(5); } function x(){ console.log(500); } console.log(x) console.log(x());//此時x變
根據js解析代碼的特點,將代碼解析成如下:
//聲明階段 function x(){//函數聲明 //console.log(5);此句會被下句代碼覆蓋 console.log(500); } var x;//變量聲明,因為x已經聲明過了,此處不進行聲明(忽略) //執行階段 console.log(x); console.log(x()); x=1; x=100;//x的值被覆蓋 console.log(x); console.log(x());//此時x變成了一個變量
所以輸出的結果就是:
function x(){
console.log(500);
}
500
100
Uncaught TypeError: x is not a function
----------------------------
所以:如果聲明了同名的函數其定義會被后者覆蓋,聲明了同名的變量其值也會被后者覆蓋