1. 程式人生 > >js基礎--型別轉換與變數作用域

js基礎--型別轉換與變數作用域

型別轉換

  • 轉換和相等性

由於JavaScript可以做靈活的型別轉換,因此其“==”相等運算子也隨相等的含義靈活多變。例如,如下這些比較結果均是true:
null==undefined//這兩值被認為相等

"0"==0//在比較之前字串轉換成數字

0==false//在比較之前布林值轉換成數字

"0"==false//在比較之前字串和布林值都轉換成數字
需要特別注意的是,一個值轉換為另一個值並不意味著兩個值相等。比如,如果在期望使用布林值的地方使用了undefined,它將會轉換為false,但這並不表明undefined==false。JavaScript運算子和語句期望使用多樣化的資料型別,並可以相互轉換。if語句將undefined轉換為false,但“==”運算子從不試圖將其運算元轉換為布林值。
  • 顯式型別轉換

儘管JavaScript可以自動做許多型別轉換,但有時仍需要做顯式轉換,或者為了使程式碼變得清晰易讀而做顯式轉換。

做顯式型別轉換最簡單的方法就是使用Boolean()、Number()、String()或Object()函式。當不通過new運算子呼叫這些函式時,它們會作為型別轉換函式做型別轉換:
Number("3")//=>3

String(false)//=>"false"或使用false.toString()

Boolean([])//=>true

Object(3)//=>new Number(3)
需要注意的是,除了null或undefined之外的任何值都具有toString()方法,這個方法的執行結果通常和String()方法的返回結果一致。

JavaScript中的某些運算子會做隱式的型別轉換,有時用於型別轉換。如果“+”運算子的一個運算元是字串,它將會把另外一個運算元轉換為字串。一元“+”運算子將其運算元轉換為數字。同樣,一元“!”運算子將其運算元轉換為布林值並取反。在程式碼中會經常見到這種型別轉換的慣用法:
x+""//等價於String(x)

+x//等價於Number(x).也可以寫成x-0

!!x//等價於Boolean(x).注意是雙歎號
如果通過Number()轉換函式傳入一個字串,它會試圖將其轉換為一個整數或浮點數直接量,這個方法只能基於十進位制數進行轉換,並且不能出現非法的尾隨字元。parseInt()函式和parseFloat()函式(它們是全域性函式,不從屬於任何類的方法)更加靈活。parseInt()只解析整數,而parseFloat()則可以解析整數和浮點數。如果字串字首是"0x"或者"0X",parseInt()將其解釋為十六進位制數[8],parseInt()和parseFloat()都會跳過任意數量的前導空格,儘可能解析更多數值字元,並忽略後面的內容。如果第一個非空格字元是非法的數字直接量,將最終返回NaN:

parseInt("3 blind mice")//=>3

parseFloat("3.14 meters")//=>3.14

parseInt("-12.34")//=>-12

parseInt("0xFF")//=>255

parseInt("0xff")//=>255

parseInt("-0XFF")//=>-255

parseFloat(".1")//=>0.1

parseInt("0.1")//=>0

parseInt(".1")//=>NaN:整數不能以"."開始

parseFloat("$72.47");//=>NaN:數字不能以"$"開始

 parseInt()可以接收第二個可選引數,這個引數指定數字轉換的基數,合法的取值範圍是2~36,例如:

parseInt("11",2);//=>3(1*2+1)

parseInt("ff",16);//=>255(15*16+15)

parseInt("zz",36);//=>1295(35*36+35)

parseInt("077",8);//=>63(7*8+7)

parseInt("077",10);//=>77(7*10+7)
  •  

變數作用域

  • 函式作用域和宣告提前

一個變數的作用域(scope)是程式原始碼中定義這個變數的區域。全域性變數擁有全域性作用域,在JavaScript程式碼中的任何地方都是有定義的。然而在函式內宣告的變數只在函式體內有定義。它們是區域性變數,作用域是區域性性的。函式引數也是區域性變數,它們只在函式體內有定義。

在函式體內,區域性變數的優先順序高於同名的全域性變數。如果在函式內宣告的一個區域性變數或者函式引數中帶有的變數和全域性變數重名,那麼全域性變數就被區域性變數所遮蓋。
var scope="global";//宣告一個全域性變數
function checkscope(){
    var scope="local";//宣告一個同名的區域性變數
    return scope;//返回區域性變數的值,而不是全域性變數的值
}
checkscope()//=>"local"
儘管在全域性作用域編寫程式碼時可以不寫var語句,但宣告區域性變數時則必須使用var語句。思考一下如果不這樣做會怎樣:
scope="global";//宣告一個全域性變數,甚至不用var來宣告
function checkscope2(){
    scope="local";//糟糕!我們剛修改了全域性變數
    myscope="local";//這裡顯式地聲明瞭一個新的全域性變數
    return[scope,myscope];//返回兩個值
}
checkscope2()//=>["local","local"]:產生了副作用
scope//=>"local":全域性變數修改了
myscope//=>"local":全域性名稱空間搞亂了
函式定義是可以巢狀的。由於每個函式都有它自己的作用域,因此會出現幾個區域性作用域巢狀的情況,例如:
var scope="global scope";//全域性變數
function checkscope(){
    var scope="local scope";//區域性變數
    function nested(){
        var scope="nested scope";//巢狀作用域內的區域性變數
        return scope;//返回當前作用域內的值
    }
    return nested();
}
checkscope()//=>"巢狀作用域"
  • 作為屬性的變數

當宣告一個JavaScript全域性變數時,實際上是定義了全域性物件的一個屬性。當使用var宣告一個變數時,建立的這個屬性是不可配置的,也就是說這個變數無法通過delete運算子刪除。可能你已經注意到了,如果你沒有使用嚴格模式並給一個未宣告的變數賦值的話,JavaScript會自動建立一個全域性變數。以這種方式建立的變數是全域性物件的正常的可配值屬性,並可以刪除它們:
var truevar=1;//宣告一個不可刪除的全域性變數

fakevar=2;//建立全域性物件的一個可刪除的屬性

this.fakevar2=3;//同上

delete truevar//=>false:變數並沒有被刪除

delete fakevar//=>true:變數被刪除

delete this.fakevar2//=>true:變數被刪除
 
  • 作用域鏈

如果將一個區域性變數看做是自定義實現的物件的屬性的話,那麼可以換個角度來解讀變數作用域。每一段JavaScript程式碼(全域性程式碼或函式)都有一個與之關聯的作用域鏈(scope chain)。這個作用域鏈是一個物件列表或者連結串列,這組物件定義了這段程式碼“作用域中”的變數。當JavaScript需要查詢變數x的值的時候(這個過程稱做“變數解析”(variable resolution)),它會從鏈中的第一個物件開始查詢,如果這個物件有一個名為x的屬性,則會直接使用這個屬性的值,如果第一個物件中不存在名為x的屬性,JavaScript會繼續查詢鏈上的下一個物件。如果第二個物件依然沒有名為x的屬性,則會繼續查詢下一個物件,以此類推。如果作用域鏈上沒有任何一個物件含有屬性x,那麼就認為這段程式碼的作用域鏈上不存在x,並最終丟擲一個引用錯誤(ReferenceError)異常。