變量詳解
關於JS中的變量
JS中的變量是松散類型的,可以存儲任何類型的數據。
JS變量松散類型的本質,決定了變量只是在特定時間用於保存特定值的一個名字而已。
由於不存在定義某個變量必須要保存何種數據類型值的規則,變量的值及其數據類型可以在腳本的生命周期內改變。
用var關鍵字聲明的變量,未經初始化時,保存的是一個特殊的值——undefined。
變量初始化僅僅是給變量賦一個值。
可以在一條語句中聲明多個變量,初始化或不初始化皆可,變量之間用逗號隔開。
使用更具語義的變量名,讓代碼的可讀性更強。
用var關鍵字聲明的變量是定義該變量的作用域中的局部變量。
當使用var關鍵字聲明了一個變量時,創建的這個變量是不可配置的,也就是說這個變量無法通過delete運算符刪除。
1 var num1 = 10; 2 (function num() { 3 var num2 = 10; 4 delete num2; 5 console.log(num2); //10 6 })(); 7 delete num1; 8 console.log(num1); //10
這表明,不管是在全局作用域,還是在局部作用域,var聲明的變量都不能通過delete刪除。
在嚴格模式下,使用delete運算符刪除var關鍵字聲明的變量會導致SyntaxError。
省略var關鍵字會導致定義一個全局變量,但是不推薦這種做法。在嚴格模式下,給未聲明的變量賦值會導致ReferenceError。
1 (function num() { 2 num2 = 10; 3 console.log(num2); //10 4 })(); 5 console.log(num2); //10
JS是基於詞法作用域的語言:全局變量在整個程序中始終是有定義的,局部變量在聲明它的函數體內以及其所嵌套的函數體內始終是有定義的。
在函數體內,局部變量的優先級高於同名的全局變量。
JS沒有塊級作用域,取而代之的是函數作用域:變量在聲明它們的函數體內以及這個函數體嵌套的任何函數體內都是有定義的。
函數內部聲明的變量和函數,要等到函數真正執行過后,才能有定義。
基本類型值與引用類型值
JS中的變量可以保存兩種不同類型的值:基本類型值和引用類型值。
在將一個值賦給變量時,解析器必須確定這個值是基本類型值還是引用類型值。
基本數據類型:Number、String、Boolean、Null、Undefiend;這5種基本數據類型是按值訪問的,因為可以操作保存在變量中的實際的值。
對於基本類型值,在復制變量的時候,會在新的變量上創建一個新值,這個新值是原值的一個副本,它們相互獨立。
基本類型值之間的比較,只是單純的值的比較。
引用類型值是保存在變量中的對象;引用類型值就是指對象。
保存引用類型值的變量,實際上保存的是一個指向該對象的指針。
當復制保存着對象的某個變量時,復制的其實是指針;復制操作結束后,兩個變量指向同一個對象。
對於引用類型值,可以為其添加屬性和方法,也可以修改或者刪除其屬性和方法。
在為對象添加屬性和方法時,操作的是實際的對象,因此,改變任何一個變量,都會影響另外一個變量。
1 var person1 = { 2 name : "CC" 3 }; 4 var person2 = person1; 5 console.log(person2.name); //"CC" 6 person1.name = "VV"; 7 console.log(person2.name); //"VV"
引用類型值的比較並非值的比較:即使兩個對象包含相同的屬性和值,它們也是不相等的;各個索引元素完全相等的兩個數組也不相等。
引用類型值的比較是引用的比較,當且僅當它們引用同一個對象時,它們才相等。
示例1:
1 var person1 = { 2 name : "CC" 3 }; 4 var person2 = { 5 name : "CC" 6 }; 7 console.log(person1==person2); //false
示例2:
1 var person1 = { 2 name : "CC" 3 }; 4 var person2 = person1; 5 console.log(person1 == person2); //true
類型檢測
typeof操作符,檢測一個變量是不是基本數據類型;具體而言,typeof操作符是確定一個變量是不是字符串、布爾值、數字、undefined的最佳工具;如果變量的值是一個函數,則會返回function;如果變量的值是null或者是一個其它的對象,則會返回object。
instanceof操作符,如果一個變量是給定的某一個引用類型的實例,則會返回true。
instanceof操作符的本質,在於確定左邊的操作數的原型鏈上是否有右邊的操作數的prototype屬性。
全局變量
當JS解析器啟動時,或者任何Web瀏覽器加載新頁面的時候,它將創建一個新的全局對象,並給他一組定義的初始屬性:
全局屬性:比如undefined、Infinite、NaN
全局函數:比如isNaN()、parseInt()、eval()
構造函數,比如Date()、String()、Array()、Object()
全局對象:比如Math、Json
初次創建的時候,全局對象定義了JS中所有的預定義全局值。
如果代碼聲明了一個全局變量,這個全局變量就是全局對象的一個屬性。
全局變量作用於全局作用域,在JS代碼中的任何地方都是有定義的。
變量的聲明、定義與初始化
聲明——給變量一個名字,宣告它從此刻開始存在;
定義——給變量分配一個存儲空間;
初始化——給變量的存儲空間賦值。
在JS中,就是這樣子:
1 var x; //聲明——變量x從現在開始存在; 2 x = "hello"; //(定義並)初始化。
由於JS是一種動態語言,其變量的類型是不固定的,可以隨着保存值的變化而變化,所以其定義變得無關緊要,以上。
變量聲明提升
JS在執行的時候,會把所有變量的聲明都提升到當前作用域的最前面。
聲明提升,這步操作是在JS引擎“預編譯”階段實現的,是在代碼開始運行之前。
比較下面兩段代碼。
1 var x = "hello"; 2 (function func() { 3 var x = "world"; 4 console.log(x); //"world" 5 })();
給上面的代碼換一下順序:
1 var x = "hello"; 2 (function func() { 3 console.log(x); //undefined 4 var x = "world"; 5 })();
上面的代碼中變量x的聲明被提升了,實際上代碼應該是這樣的:
1 var x = "hello"; 2 (function func() { 3 var x; //變量聲明提升 4 console.log(x); //undefined 5 x = "world"; //初始化語句 6 })();
小tips:特意將變量聲明放在函數體頂部,將賦值語句靠近放在使用變量之處,這種做法使得源代碼非常清晰地反映了真實的變量作用域。
名字解析順序
JS中一個名字可能以4種方式進入作用域,其優先級如下:
(1)語言內置——所有作用域中都有arguments對象和this對象;
(2)函數聲明
(3)形式參數
(4)變量聲明——不會影響同名的函數聲明或者形式參數聲明