年關將近,金三銀四跳槽季要來了,開始整理面試題
基礎型別有哪些?
基本型別有七種
null
undefined
boolean
number
string
symbol
(較新標準)BigInt
(新標準)
NaN
也屬於 number
型別,並且 NaN
不等於自身
基礎型別的特性是什麼?
- 基本型別的值是儲存在
棧記憶體
中的簡單資料段 - 基礎型別是
不可變的
- 即不能強行修改:
Array.prototype.sort.call('abc');
(會報錯)
- 即不能強行修改:
- 基礎型別上沒有
__proto__
沒有屬性
- 基礎型別可以通過
基本包裝型別
訪問的屬性/方法// 通過包裝型別訪問基礎型別特性 let str = 'abc'; console.log(str.length) // 當你呼叫 `str.length` 時,實際過程是這樣的: // -> 建立String型別的一個例項 // -> 在例項上呼叫指定的方法 // -> 銷燬這個例項 let _str = new String(str); let len = _str.length; _str = null; 複製程式碼
怎麼檢測基礎型別?
- 檢測基礎型別可以用
typeof
,但是typeof null === 'object'
null
是基礎型別,不是 Object- 由於null的歷史遺留問題(前三位為000),所以使用
typeof
檢測null會產生BUG
// 借鑑 Vue 原始碼的 object 檢測方法 function isObject (obj: any): Boolean { return obj !== null && typeof obj === 'object' } 複製程式碼
- 通過
Object.prototype.toString.call
(萬能方法)- 檢測
[[class]]
- 在不覆蓋 toString 方法前提下,任何一個物件呼叫 Object 原生的 toString 方法都會返回
[object type]
// 借鑑 Vue 原始碼的檢測方法 let _toString = Object.prototype.toString; function toRawType (value: any): String { // 獲取 從第九個到倒數第二個 字元 // 比如 [object String] 獲取 String return _toString.call(value).slice(8, -1) } 複製程式碼
- 檢測
基礎型別是如何轉換的?
- 基本型別轉換時,首先會呼叫
valueOf
,然後呼叫toString
。(這兩個方法可以被重寫) - 在四則運算中,除了
+
其他操作都會以數字進行計算,如果是+
運算,如果不是所有字面量都是number
(都是number就是數字的加法咯),那麼會轉換為字串(toString
)進行拼接
為什麼 0.1 + 0.2 為什麼不等於 0.3 ?
遵循IEEE 754 雙精度版本(64位)
標準的語言都有的問題。計算機無法識別十進位制,JS會將十進位制轉換為對應的二進位制(二進位制即:0
和 1
)。
那麼 怎麼用 0
和 1
來表示 0.1
和 0.2
呢?
console.log(0.1.toString(2));
// -> 0.0001100110011001100110011001100110011001100110011001101
console.log(0.2.toString(2));
// -> 0.001100110011001100110011001100110011001100110011001101
複製程式碼
這樣看似沒問題啊。為什麼會有BUG呢?
別忘了:JS的精確度區間 約為正負 2^53
,超出限制會截斷。所以你看到的 0.1 不是真的 0.1。
那麼怎麼解決 JS 的精確度問題?
- 目前主流的解決方案是
先乘再除
- 比如精確到小數點後2位
- 先把需要計算的數字都 乘1000
- 計算完成後再把結果 除1000
- 使用新基礎型別
BigInt
(相容性很差)
JS的"真"值有哪些?
JS中除了 "假" 值以外就是 "真" 值。
"假"值包括 7 個
undefined
null
false
NaN
''
0
-0
在條件判斷的隱式轉換中:"假" 值會轉換為 false
,"真" 值會轉換為 true
;
那說一下引用型別吧?
- 除了基礎型別,都是引用型別。
- 引用型別正在建立時會分配
兩個空間
- 一塊在
堆
上,儲存引用型別本身的資料(當然資料量會比較大) - 一塊在
棧
上,儲存對堆
上資料的引用(儲存堆上的記憶體地址,也就是指標)
- 一塊在
- 引用型別是可變的:即
let a={}; a.x=1;
- function引數是值傳遞,要注意不能修改引用
怎麼檢測引用型別?
- 通過
Object.prototype.toString.call
檢測[[class]]
- 通過
instanceof
判斷引用型別 - 通過
constructor
判斷引用型別(constructor
是可寫的,慎用)
instanceof 的原理是什麼?
instanceof
內部機制是通過判斷物件的原型鏈中是不是能找到對應的的prototype
所以在驗證iframe時會有BUG,因為 window.Array.prototype !== window.frames[0].Array.prototype
,所以不存在繼承關係
// 實現 instanceof
function instanceof(obj, target) {
// 獲得物件的原型
obj = obj.__proto__
// 判斷物件的型別是否等於型別的原型
while (true) {
// 如果__proto__ === null 說明原型鏈遍歷完畢
if (obj === null) {
return false
}
// 如果存在 obj.__proto__ === target.prototype
// 說明物件是該型別的例項
if (obj === target.prototype) {
return true
}
// 原型鏈上查詢
obj = obj.__proto__
}
}
複製程式碼
如果用 instanceof 判斷基礎型別會怎麼樣?
會返回 false
因為基礎型別沒有 __proto__
let str = '123';
console.log(str instanceof String) // -> false
複製程式碼
但是如果更改了 靜態方法Symbol.hasInstance
就可以判斷了
class StringType {
static [Symbol.hasInstance](val) {
return typeof val === 'string'
}
}
console.log(str instanceof StringType) // -> true
複製程式碼
說一下陣列吧?
陣列是一種類列表物件,其資料在記憶體中也可以不連續
陣列應該是一段線性分配的記憶體,但是JS的Array的檢索和更新方式和物件一模一樣
- Array它把下標變成數字,用其作屬性。它比真正的陣列慢,但用起來更方便。
- Array本質還是物件,其原型繼承自
Array.prototype
,向上再繼承自Object.prototype
- Array的方法是設計為物件通用的,物件也能呼叫陣列的方法
let obj = { '2': 3, '3': 4, 'length': 2, 'splice': Array.prototype.splice, 'push': Array.prototype.push } obj.push(1) obj.push(2) console.log(obj); // Object(4) [empty × 2, 1, 2, splice: ƒ, push: ƒ] 複製程式碼
- 使用
delete arr[2]
,並不能減少length,而只是刪除了對應的屬性(變成empty)
什麼是類陣列?
- 類陣列不是陣列,通過
Array.isArray()
會返回false
- 類陣列通過
Array.from
可以轉換為陣列 - 屬性要為索引(數字)屬性
- 必須有length屬性
經常遇見的類陣列
- 字串
- 唯一的原生類陣列
arguments
- arguments完全可以使用
...args
代替,這樣不定引數就是真陣列 - arguments在箭頭函式中被移除
- arguments完全可以使用
- DOM
[] == ![] 結果是什麼?
PS: 感謝 @沉末_ 的補充
型別轉換都是先 valueOf
再 toString
;
右邊
- 由於
!
優先順序比==
高,先執行!
![]
得到 false- 進行 相等性判斷
false
轉化為數字0
左邊
- 執行
[].valueOf()
原始值 還是 [] - 執行 [].toString() 得到 ''
''
轉化為數字0
所以:0 == 0
,答案是 true
驗證:
let arr1 = [];
let arr2 = [];
console.log(arr1 == !arr2) // -> true
arr1.toString = () => {
console.log(111)
return 1
}
console.log(arr1 == !arr2)
// -> 111
// -> false
複製程式碼
== 和 === 的區別 ?
===
不進行隱式轉換==
會進行隱式轉換{a: 1} == "[object Object]"
左邊會執行.toString()
如何讓 (a == 1 && a == 2)條件成立?
依然是型別轉換邏輯:基礎型別通過 valueOf
進行隱式轉換
更改 valueOf
方法就可以實現
let a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
console.log(a == 1 && a == 2);
複製程式碼
Object.is
和 === 的區別 ?
Object.is(v1, v2)
修復了 ===
的一些BUG (-0和+0, NaN和NaN)
:
// === 案例
-0 === +0 // -> true
NaN !== NaN // -> false
Object.is(-0, +0) // -> false
Object.is(NaN, NaN) // -> true
複製程式碼