前言:這是筆者學習以後本身的理解與整理。若是有錯誤或者疑問的地方,請你們指正,我會持續更新!javascript
聲明的意思是宣稱一個變量名的存在,定義則爲這個變量分配存儲空間,初始化則是給該變量名的存儲空間賦予初始值;java
javascript 中,變量沒有固定類型,其存儲空間會隨着初始化(並賦值)而變化;面試
<script type="text/javascript"> var a;//聲明a console.log(a);//undefined; 只聲明,沒有賦值,返回undefined a = 'hello world!';//(定義)初始化變量a而且賦值'hello world!' console.log(a);//hello world! var a;//再次聲明a,但沒有賦值,a的值不會被清空 console.log(a);//hello world! </script>
javascript 代碼在執行時,通常是一行一行往下執行的,可是這裏面又有一個變量聲明提高的概念;express
javascript 引擎在執行時,會把全部變量聲明和函數聲明都提高到當前做用域的最前面(hoisting 機制);瀏覽器
<script type="text/javascript"> // console.log(a);//a is not defined a沒有聲明 console.log(b);//undefined 沒找到b(沒有初始化賦值),但已經聲明瞭 var b = 'hello world!'; //聲明並初始化b賦值'hello world!' console.log(b);//hello world! </script>
上面代碼中 a 和 b 的區別就是b聲明提早了,a 沒有聲明;因此上面代碼至關於:函數
<script type="text/javascript"> var b;//聲明b // console.log(a);//a is not defined a沒有聲明 console.log(b);//undefined b = 'hello world!'; //初始化並賦值'hello world!' console.log(b);//hello world! </script>
一直對一些基本的概念的理解有些模糊,先梳理如下:學習
函數聲明(function statement),使用function關鍵字聲明一個函數,再指定一個函數名,叫函數聲明。如:function fnName(){};spa
但須要注意的是:code
非嚴格模式下:blog
<script type="text/javascript"> //非嚴格模式 if(true){ function find(){ console.log(456); } } find();//456 if(false){ function cantFind(){ console.log(123); } } cantFind();//TypeError: cantFind is not a function </script>
嚴格模式下:
<script type="text/javascript"> 'use strict' //嚴格模式下 if(true){ function find(){ console.log(456); } } find();//ReferenceError: cantFind is not defined </script>
函數表達式(function expression),把一個匿名函數當作值傳給一個變量,叫函數表達式,這是最多見的函數表達式語法形式。如:var fnName = function(){};
<script type="text/javascript"> //在function前面加!、+、 -、=甚至是逗號等到均可以將函數聲明轉換成函數表達式,咱們通常用()或者 = var a = function b(){ return b; } console.log(a());//function b(){console.log(b);} console.log(b());//ReferenceError: b is not defined //函數表達式的標識符在外部做用域是看不到的,只能在內部做用域看到 </script>
函數聲明提高有兩種狀況:1.函數聲明;2.把函數做爲一個值傳給一個變量(函數表達式);
和變量聲明提高同樣,函數聲明也會提高,而且定義也會提高(這個說法也不太準確,看下面的例子);
<script type="text/javascript"> play();//函數聲明提高了,之因此能夠正常運行,是由於定義也被提早了 function play(){ console.log('hello world!');//hello world! } </script>
以上代碼中函數play()聲明提高了,之因此能夠正常運行,是由於函數聲明提高了,定義也被提高了(這個說法也不太準確,看下面的例子),以上代碼至關於:
<script type="text/javascript"> function play(){ console.log('hello world!');//hello world! } play(); </script>
咱們有時候會把一個函數做爲值傳給一個變量,這時變量會被聲明提高,但函數不會,它只是一個函數表達式,咱們只須要它的返回值給變量;
若是它是一個具名函數表達式,那麼函數表達式的標識符(函數名)在外部做用域是找不到的,只在內部做用於能找到;
<script type="text/javascript"> //console.log(play);//ReferenceError: play is not defined 聲明錯誤 //play();//ReferenceError: play is not defined 聲明錯誤 console.log(games);//undefined 變量聲明瞭,但沒賦值,因此沒找到 //games();//TypeError: games is not a function 類型錯誤 var games = function play(){ //函數表達式 console.log(typeof play); } //play();//ReferenceError: play is not defined 聲明錯誤 games();//function 函數表達式的標識符(函數名)在外部做用域是找不到的,只在內部做用於能找到; </script>
上面寫的函數聲明的定義也被提高了,這個說法並不太準確,
<script type="text/javascript"> if (!("a" in window)) { function a() { return '爲何下面彈出的a是undefined'; } } console.log(a); //undefined //若是定義也被提早,這裏應該是function a(){},因此這裏定義並無被提早,只有函數名被提早了,而後if語句進不去了, //由於函數聲明能夠和函數表達式相互轉換,只是寫法不一樣而已, //因此上面的代碼有個說法,這裏的函數a能夠理解成var a = function(){},函數聲明的方式提早的是函數名,然而這又不能解釋爲何函數能在函數聲明以前能被調用; b(); function b(){ console.log('函數聲明的疑問,爲何b能執行'); } </script>
由於函數聲明能夠和函數表達式相互轉換,只是寫法不一樣而已,因此上面的代碼有個說法,函數a能夠理解成 var a = function(){},函數聲明的方式提早的是函數名,然而這又不能解釋爲何函數能在函數聲明以前能被調用;
同一做用域下,相同變量名,後面的賦值會覆蓋前面的;
同一做用域下,若是變量名和函數名相同,那麼函數名會覆蓋變量名,不論它們在代碼的順序如何;可是它們的初始化賦值是按順序執行的;
因此,同一做用域下應避免使用相同變量名;
<script type="text/javascript"> console.log(a);//function a(){console.log('我是函數');} 函數名和變量名相同,函數名會覆蓋變量名 var a = 'hello world!'; function a(){ console.log('我是函數'); }; console.log(a);//hello world! a();//TypeError: a is not a function 類型錯誤 </script>
上面代碼中,a的值最後是字符串,緣由就在於:
上面的代碼至關於:
<script type="text/javascript"> console.log(a);//function a(){console.log('我是函數');} 函數名和變量名相同,函數名會覆蓋變量名 function a(){ console.log('我是函數'); }; var a = 'hello world!'; console.log(a);//hello world! a();//TypeError: a is not a function 類型錯誤 </script>
有一道經典面試題:
<script type="text/javascript"> b = c; console.log(b); console.log(c); b(); console.log(a); function c() { a = 1, b = 2, c = 3; }; </script>
函數c還沒運行,因此裏面的變量是不知道的,可是函數c已經聲明提早了,又由於第一行b=c,因此b和c都是函數c ;
函數c運行以後,裏面的變量a,b,c被發現是全局變量,而且給它們賦值,因此外部能找到a=1,而且你還會發現b=2; c=3;
若是函數c未曾運行過,那麼是找不到它們的 ;
這道題換一下:
<script type="text/javascript"> b = function c() { a = 1, b = 2, c = 3; }; b(); console.log(a); console.log(b); console.log(c); </script>
函數 b 運行以後,發現了裏面的全局變量,而且給它們賦值 a=1; b=2;
可是有個特殊的變量 c,c 是函數表達式的名字,函數的名字,規範說明函數名不能被改變,因此這裏設置 c=3 實際上是對函數名賦值,是無效的,也不會被當作全局變量;並且函數表達式的標識符只有內部做用域能夠找到,因此外部是找不到的,報錯 ReferenceError: c is not defined 引用錯誤;