[課程筆記]克服JS的奇怪部分-CH2 型別與運算子
本篇筆記目錄:
U8 純值
觀念小叮嚀:運算子
U9 運算子的優先性與相依性
觀念小叮嚀:強制型轉
U10 比較運算子
U11 存在與布林
U12 預設值
框架小叮嚀:預設值
觀念小叮嚀:型別與Javacript
JS 是 Dynamic Typing
Dynamic Typing 動態型別
我們不需要告訴 JS 你的變數是何種型別的資料,相反地,它會在運行程式時自動知道,所以當你執行程式時,一個變數可以在不同時候擁有不同型別,因為型別都是在執行時才知道的!
其他程式語言像 Java 或 C# 它們用靜態型別的方式處理,一開始就要告訴編譯器你的變數是什麼資料行別,
U8 純值
純值是一種資料的型別,表示一個值,換句話說,不是物件
JS 有六種純值(primitive types,或稱基本型別)
- undefined
- null
- boolean
- number:為一個數值型態,它是浮點數(永遠有小數點在後面)
- string
- sybmol:ES6 新增的純值
觀念小叮嚀:運算子
運算子是一個特殊的函數,通常需要兩個參數來回傳一個值
a+c
如何運作?可以想成這樣:
//+ 是函數名稱
function +(a,c){
return //add the two number
}
+(a,c)
但這樣寫太麻煩,所以和大部分的程式語言一樣,我們只需要使用中綴表示法(將函數名稱寫在兩個參數之間)
//拿掉括號和逗號,將函數名稱寫在兩個參數之間
a+c
U9 運算子的優先性與相依性
Precendence 優先性
在同一行不只有一個運算子時,哪個運算子被優先運算
var a=3+4*5
console.log(a)
//23
具有高優先性的會優先運算,
+ 為13 * 為14
*大於+,所以 4*5 先執行然後回傳20 ,然後執行 3+20 再回傳23
括號為優先性為19,所以裡面的東西永遠最先計算
Associativity 相依性
當運算子優先性相同時,運算子被計算的順序
左到右的相依性:左相依性
右到左的相依性:右相依性
var a=2
var b=3
var c=4
a=b=c
console.log(a);
console.log(b);
console.log(c);
//4
//4
//4
為什麼會輸出4呢?因為 = 的相依性為 右相依性,所以會先執行 b=c 並回傳 4,再執行 a=4, 回傳 4!
觀念小叮嚀:強制型轉
Coercion 強制行轉
轉換一個值的型別(例:數值轉成字串)
var a=1+'2'
console.log(a)
//12
//第一個數值被強制行轉成字串
U10 比較運算子
console.log(1<2<3)
//true
console.log(3<2<1)
//true
為何第二個回傳true 呢?
1.「 <」為左相依性,所以從左開始看起
- 3<2 回傳 false,false<1,出現非預期型別,boolean 值被強制型轉
我們可以使用 Number() 觀察,Number() 可以將東西轉換成數值
Number('3')
//3
Number('false')
//0
Number('true')
//1
會發現 boolean 值會被強制型轉成 1 和 0
所以當 false<1 時,實際執行的是 0<1,回傳 true
我們回頭再看console.log(1<2<3),1<2 回傳true ,true<3 是1<3 回傳 true
其他強制型轉的情況:
Number(undefined)
//NaN:代表無法轉換成數值
Number(null)
//0
因為強制型轉的關係,我們會很難除錯,像是:
var a= false;
a==0
//true
""==0
//true
""==false
//true
所以99%時間我們會使用 =
(strict equality) 來比較相等性,它會同時判斷型別是否相同
3=3
//true
3==='3'
//false
U11 存在與布林
所有不存在的東西都會被轉換成 null
Boolean(undefined)
//false
Boolean(null)
//false
Boolean("")
//false
Boolean("hi")
//true
而我們可以利用這個特性去檢查東西是否存在
var a;
//code to find a's value
if(a){
console.log('sth is here')
}
在 if 陳述句括號裡面的東西,它會試著轉換為布林,如果最後a是undefined(因為中間未設值),或是 null,”“,我們都會得到回傳的false
但有個要注意的地方 Boolean(0) 也會回傳 false,所以我們可以寫成這樣,判斷是否為0
var
//code to find a's value
if(a||a===0){
console.log('sth is here')
}
U12 預設值
不同於其他程式語言,JS 在我們沒有放入參數時,不會丟出錯誤訊息!
function greet(name){
console.log(name)
console.log('hello'+name);
}
greet()
//undefined
//Hello undefined
執行過程:
- greet() 被呼叫,一個新的執行環境被創造
- JS 忽略了我們沒有用參數呼叫這個函數,name 在記憶體中被設定為 undefined
- undefined 被強制轉型為字串,然後被和”hello”放在一起
如果我們想要一個設一個預設值給這個參數呢?
ES6 中有可以設定預設值的方法:
function green(name='jack'){
console.log('Hello'+name)
}
green();
但在舊的方法中我們可以這麼使用:
function greet(name){
name= name||'<Your name here>'
console.log('hello'+name);
}
greet()
//'hello <Your name here>'
為什麼呢?因為 || 不只是回傳 true 或 false ,它還可會回傳可以被強制轉型為true的值:
true||false
//true
undefined||'hello'
//'hello'
//當他非 boolean 值時,回傳可以被強制轉型為true的值
'hi'||'hello'
//'hi'
//當他非 boolean 值時,回傳第一個可以被強制轉型為true的值
所以我們回頭看這行
name= name||'<Your name here>'
當 name 可以被強制轉型為true 時,回傳 name,如果 name 是 undefined 時,回傳 '<Your name here>'
。
框架小叮嚀:預設值
我們已經學過的東西如何應用在受歡迎的框架中?
假設lib1和lib2是我的兩個框架:
<script src="lib1.js></srcipt>
<script src="lib2.js></srcipt>
<script src="app.js></srcipt>
//lib1.js(假裝一個libary)
var libaryName='Lib 1'
//lib2.js(假裝另一個libary)
var libaryName='Lib 2'
//app.js
console.log(libaryName)
//'Lib 2'
這三個 script 標籤不會創造新的執行環境,它們會把程式碼堆在對方上面,結合和最小化你的JS程式成為一個檔案,再執行,所以 libaryName 會被視為全域環境的全域變數,在Lib2.js中被重新附值。
這在框架中很不尋常,可能會讓我框架互相衝突
解決方法:
測試libaryName
是否已經在全域執行環境、全域變數裡
//lib2.js
window.libaryName=window.libaryName||'Lib 2'