耶耶「給自己看的」系列重開 XD
發現之前做過的筆記有一點點重覆:
[Week2] 給自己看的 JavaScript 筆記 - 迴圈、函式、其他觀念
變數型態
七種資料型態:
primitive type 原始型態
- null
- undefined
- string
- number
- boolean
- symbol(ES6)
其他都是 boject 物件
- object(array, function, date ...)
原始型態都是 Immutable ,也就是不能改變。
let a = 10
a = 20
是重新賦值而非改變,改變例如:
var str = 'hello'
str.toUpperCase()
consoel.log(str) // 'hello'
上面例子中第二行雖然會回傳新字串 HELLO
,但並不會影響 str
本身。
var str = 'hello'
var newStr = str.toUpperCase()
consoel.log(newStr) // 'HELLO'
array 因為不是原始型態,因此可以被改變:
var arr = ['a']
arr.push('b')
console.log(arr) / ['a', 'b']
偵測型態
typeof
可以偵測型態:
console.log(typeof 10) // number
console.log(typeof []) // object
console.log(typeof function() {}) // function
不過有些小 bug 例如:
console.log(typeof null) // object
MDN - typeof 對此解釋如下:
自從 JavaScript 一開始出現, JavaScript 的值就總以型別標簽跟著一個值的方式表示。物件的型別標簽是 0. 而 null 這個值是使用 NULL 指標 (在大部份平台上是 0x00) 來表示. 因此, null 看起來像是一個以 0 為型別標簽的值, 並使得 typeof 傳回不甚正確的結果.
typeof
時常用在檢測錯誤上。例如 undefined 不能直接被 console.log 出來,此時就可以先用 typeof :
// a 沒有被宣告過,因此 a !== undefined 也是 true
if (typeof a !== 'undefined') {
console.log(a)
}
就可以避免噴錯了。
不過 typeof 無法檢測一個變數是不是 Array ,因此可以用:
conaole.log(Array.isArray([])) // true
這裡要注意有些比較舊的瀏覽器上沒有這個方法。
另一個檢測型態的方法:
console.log(Object.prototype.toSting.call(<欲檢測物>))
console.log(Object.prototype.toSting.call(1)) // [Object Number]
console.log(Object.prototype.toSting.call('a')) // [Object String]
console.log(Object.prototype.toSting.call(null)) // [Object Null]
賦值 =
primitive type 的賦值是直接儲存在記憶體中,因此賦值時很直觀:
var a = 10
var b = a
b = 20
console.log(a, b) // 10 20
但 object 和 array 就沒那麼簡單了:
var a = { num: 10 }
var b = a
b.num = 20
consolo.log(a, b) // { num: 20 } { num: 20 }
這四行電腦實際在做的事情如下:
- 內容
{ num: 10 }
存入某個記憶體位置0x01
中。 - 指定 b 的內容也是在
0x01
。 - 存取 b 所指向的
0x01
中的內容,將 num 改成 20 。 - 找 a 時就是叫出
0x01
的內容,因此找到{ num: 20 }
。
不過,如果指定 b 的新值時用別的方法,結果又會不一樣了。
var a = { num: 10 }
var b = a
b = { num: 20 }
consolo.log(a, b) // { num: 10 } { num: 20 }
因為對電腦來說,這四行是在:
- 內容
{ num: 10 }
存入某個記憶體位置0x01
中。 - 指定 b 的內容也是在
0x01
。 - 重新指定 b 的記憶體位置,指向
0x02
,並將{ num: 20 }
放入。 - 找 a 時就是叫出
0x01
的內容,因此找到{ num: 10 }
。
另外,若在判斷時使用 =
:
if (a = 20) {
consoel.log('hihi') // hihi
}
對電腦來說:
- 將 a 賦值 20 。
- 判斷 a 的值, 20 對應到的 boolean 值是 true。
- 執行 console.log 。
== 和 ===
console.log(2 == '2') // true
console.log(2 === '2') //false
==
會先將兩邊的型態轉換成一樣的,但 ===
不會,因此會連型態一起檢查。因此會優先推薦使用 ===
最嚴謹,比較不會出 bug 。
值得注意的是,非 primitive type 的物件中, ===
比較的實際上不是內容,而是記憶體位置。因此:
var arr1 = [1]
var arr2 = [1]
console.log(arr1 === arr2) // false
arr2 = arr1
console.log(arr1 === arr2) // true
也因此 {} === {}
的結果會是 false 。
特殊例子
NaN
是一種數字,表示它並不是一個數字,例如:
var a = Number('hi')
console.log(typeof a) // NaN
此時來看看:
console.log(a === a) // false
NaN 不等於任何東西,包含它自己。
若要檢測某個東西是否為 NaN 可用 isNaN()
。
var 、 let 和 const
作用域:變數的生存範圍
ES6 以前作用域的基本單位是 function ,出了 function 就失去作用。
在任何 function 外的變數被稱為全域 (global) 變數,其在所有 function 內皆適用(因為會往上找)。
var a = 20
function test() {
var a = 10
console.log(a)
}
test() // 10
console.log(a) // 找到全域變數的 a 是 20
但若第三行不是再宣告一次變數 a
而是重新賦值,結果會如下:
var a = 20
function test() {
a = 10
console.log(a)
}
test() // 10 ,還順便更改了 a 的值
console.log(a) // 10
如果在 function 內沒有使用 var
宣告,會自動被認為是全域變數:
function test() {
a = 10 // 變成全域變數
console.log(a)
}
test() // 10
console.log(a) // 10
來看個複雜一點的例子:
var a = 'global'
function test() {
var a = 'test scope a'
var b = 'test scope b'
console.log(a, b) // test scope a test scope b
function inner() {
var b = 'inner b'
console.log(a, b) // test scope a inner b
}
inner()
}
test()
在 inner()
中,因為找不到 a 這個變數,所以往上一層找到 var a = 'test scope a'
。往上找的過程稱為 scope chain
。
另外, scope 之間也不會共享變數, scope chain 和在哪裡有關,而不是在哪裡被呼叫。
var a = 'global'
function change() {
var a = 'change'
test()
}
fuction test() {
console.log(a) // global
}
change()
上面例子中,因為 test scope 中沒有定義 a ,因此會往上找到全域變數 var a = 'global'
,和 change() 中的 a 一點關係也沒有。
回到 const 和 let
var 的作用域在 function 之中,但 const 和 let 的作用域僅限 block (看到 {}
都算)。
var a = 60
if (a === 60) {
var b = 10
}
consoel.log(b) // 10
// 另一個情境
var a = 60
if (a === 60) {
let b = 10 // 只在 if 裡面可用
}
consoel.log(b) // 錯誤
另外, constance
是常數,宣告 const
時一定要給一個初始值,而且一旦給定就不能變動。
不過因為 object 的儲存方式是依賴記憶體位置,因此只要記憶體沒變就可:
conat arr = [1]
arr.push(2) // 可以
arr = [1, 2] // 不行,噴 error