前言
目前的前端世界,三大框架橫行,原生JavaScript所用越來越少,但我認為JavaScript作為每一個前端工程師的立身之本,學再多遍都不為過,
因此我決定整理JavaScript中容易忽視或者混淆的知識點,寫一系列文章,以靈魂追問的方式,系統且完整的帶大家遨游不一樣的JavaScript,
JS型別之問——檢測篇
第一問:js中的資料型別有哪些?
- 原始資料型別:共有7種
Boolean Number String undefined null Bigint Symbol
- 參考資料型別:1種
Object物件(包括普通Object、Function、Array、Date、RegExp、Math)
第二問:你真的懂typeof嗎?
-
typeof的作用?區分資料型別,可以回傳7種資料型別:
number、string、boolean、undefined、object、function,以及ES6新增的symbol -
typeof能正確區分資料型別嗎?不能,對于原始型別,除
null都可以正確判斷;對于參考型別,除function外,都會回傳"object" -
typeof注意事項typeof回傳值為·格式,注意類似這種考題:typeof(typeof(undefined)) -> "string"typeof未定義的變數不會報錯,回傳"undefiend"typeof(null) -> "object": 遺留已久的bugtypeof無法區別陣列與普通物件:typeof([]) -> "object"typeof(NaN) -> "number"
習題
console.log(typeof(b));
console.log(typeof(undefined));
console.log(typeof(NaN));
console.log(typeof(null));
var a = '123abc';
console.log(typeof(+a));
console.log(typeof(!!a));
console.log(typeof(a + ""));
console.log(typeof(typeof(null)));
console.log(typeof(typeof({})));
答案
undefined // b未定義,回傳undefined
undefined
number // NaN 為number型別
object
number // +a 型別轉換為NaN
boolean
string
string // typeof(null) -> "object"; typeof("object") -> "string"
string
第三問:什么是instanceof?你能模擬實作一個instanceof嗎?
instanceof可以準確判斷物件的型別,其內部機制是判斷物件的原型鏈上是否存在該型別的原型,instanceof常用來判斷A是否為B的實體:
// A是B的實體,回傳true,否則回傳false
// 判斷A的原型鏈上是否有B的原型
A instaceof B
- 模擬實作
instanceof
思想:沿原型鏈往上查找
function instance_of(Case, Constructor) {
// 基本資料型別回傳false
// 兼容一下函式物件
if ((typeof(Case) != 'object' && typeof(Case) != 'function') || Case == 'null') return false;
let CaseProto = Object.getPrototypeOf(Case);
while (true) {
// 查到原型鏈頂端,仍未查到,回傳false
if (CaseProto == null) return false;
// 找到相同的原型
if (CaseProto === Constructor.prototype) return true;
CaseProto = Object.getPrototypeOf(CaseProto);
}
}
測驗:
console.log(instance_of(Array, Object)) // true
function User(name){
this.name = name;
}
const user = new User('zc');
const vipUser = Object.create(user);
console.log(instance_of(vipUser, User)) // true
第四問:如何區分陣列與物件?instanceof判斷是否是陣列可靠嗎?
Array.isArray()- 如果不存在
Array.isArray()呢?可以借助Object.prototype.toString.call()實作,這種實作方式兼容性最好
if (!Array.isArray) {
Array.isArray = function(o) {
return typeof(o) === 'object'
&& Object.prototype.toString.call(o) === '[object Array]';
}
}
instanceof判斷
判斷方式
// 如果為true,則arr為陣列
arr instanceof Array
instanceof判斷陣列如此之簡單,為何不推薦使用那?
instanceof運算子的問題在于,如果網頁中存在多個iframe,那便會存在多個Array建構式,此時判斷是否是陣列會存在問題,
更詳細的內容可以參考博文:JavaScript為啥不用instanceof檢測陣列
第五問:如何判斷一個數是否為NaN?
NaN有個非常特殊的特性,NaN與任何值都不相等,包括它自身
NaN === NaN // false
鑒于這個獨特的特性,可以手撕一個比較簡單的判斷函式
function isNaN(x) {
return x != x;
}
-
isNaN方法:不推薦使用,MDN對它的介紹是:isNaN函式內包含一些非常有趣的規則,但為了避免一些面試官出一些冷門題目,稍微介紹一下,
isNaN的有趣機制:會先判斷引數是不是Number型別,如果不是Number型別會嘗試將這個引數轉換為Number型別,之后再去判斷是不是NaN,舉個例子:
// 是不是很有趣 console.log(isNaN([])) // false console.log(isNaN([1])) // false console.log(isNaN([1, 2])) // true console.log(isNaN(null)) // false console.log(isNaN(undefined)) // trueisNaN的結果很大程度上取決于Number()型別轉換,后面會專門有一部分來介紹, -
Number.isNaN(推薦使用)
與isNaN()相比,Number.isNaN()不會自行將引數轉換成數字,只有在引數是值為NaN的數字時,才會回傳true,
第六問:如何實作一個可靠的型別判斷函式
利用Object.prototype.toString.call([value]),可以精準判斷資料型別,大家可以根據這個原理封裝一個自己的type方法,
toString.call(()=>{}) // [object Function]
toString.call({}) // [object Object]
toString.call([]) // [object Array]
toString.call('') // [object String]
toString.call(22) // [object Number]
toString.call(undefined) // [object undefined]
toString.call(null) // [object null]
toString.call(new Date) // [object Date]
toString.call(Math) // [object Math]
toString.call(window) // [object Window]
JS型別之問——轉換篇
第七問:toString和valueOf的什么情況下會自動呼叫,優先級怎樣?
- 介紹:這兩個方法屬于
Object物件,是為了解決JavaScript值運算與顯示的問題,為了更適合自身功能,很多JavaScript內置物件都重寫了這兩個方法, toString(): 回傳當前物件的字串形式;valueOf(): 回傳該物件的原始值- 各個型別下兩個方法回傳值情況對比
| 型別 | valueOf | toString |
|---|---|---|
| Array[1,2,3] | (3) [1, 2, 3] | 1,2,3 |
| Object | 物件本身 | [object Object] |
| Boolean型別 | Boolean值 | “true"或"false” |
| Function | 函式本身 | function fnName(){code} |
| Number | 數值 | 數值的字符換表示 |
| Date | 毫米格式時間戳 | GMT格式時間字串 |
-
自動呼叫
隱式轉換時會自動呼叫toString和valueOf方法,兩者優先級如下:
- 在進行
object格式轉換時,優先呼叫toString方法 - 強制轉化為字串型別時,優先呼叫
toString方法 - 強制轉換為數值型別時,優先呼叫
valueOf方法 - 使用運算子運算子情況下,
valueOf優先級高于toStirng
- 在進行
-
經典案例
const a = {x:1};
const b = {x:2};
const obj = {};
obj[a] = 100;
obj[b] = 200;
console.log(obj[a]);
console.log(obj[b]);
JavaScript中物件的鍵都為字串格式,需要由物件轉換為字串,呼叫Object上的toString方法,
根據轉換規則,a和b都轉換為[object Object],因此obj[a]和obj[b]操作的是同一個鍵,最終列印兩個200
第八問:你知道物件轉換成原始值是什么流程嗎?
物件轉換成數字型別,會按照下面的步驟執行(三步走戰略):
- 先呼叫物件的
Symbol.toPrimitive方法,如果不存在這個方法(toPrimitive,可以參考博客從ECMA規范徹底理解 JavaScript 型別轉換
) - 呼叫物件的
valueOf(),如果轉換為原始型別,則回傳 - 呼叫物件的
toString(),如果轉換為原始型別,則回傳 - 如果都沒有回傳原始型別,會報錯
第九問:你能理清型別轉換嗎?
首先需要知道:在JavaScript中,只有三種型別的轉換
- 轉換為
Number型別:Number() / parseFloat() / parseInt() - 轉化為
String型別:String() / toString() - 轉化為
Boolean型別:Boolean()
因此遇到型別轉換問題,只需要弄清楚在什么場景之下轉換成那種型別即可,
轉換為boolean
- 顯式:
Boolean方法可以顯式將值轉換為布爾型別 - 隱式:通常在邏輯判斷或者有邏輯運算子時觸發(
|| && !)
Boolean(1) // 顯式型別轉換
if (1) {} // 邏輯判斷型別觸發隱式轉換
!!1 // 邏輯運算子觸發隱式轉換
1 || 'hello' // 邏輯運算子觸發隱式轉換
boolean型別只有true和false兩種值,
除值**0,-0,null,NaN,undefined,或空字串("")**為false外,其余全為true
轉化為string
- 顯式:
String方法可以顯式將值轉換為字串 - 隱式:
+運算子發下有兩種情況可以觸發(這里的+并非取正數運算子)- 有一側運算元為
string型別 - 有一側運算元為物件
- 有一側運算元為
轉化為string型別的本質:需要轉換為string的部分呼叫自身的toString方法(null/undefined回傳字串格式的null和undefined)
String([1,2,3]) // 1,2,3
String({x:1}) // [object Object]
1 + '1' // 11
1 + {} // 1[object Object]
轉化為number
- 顯式:
Number方法可以顯式將值轉化為數字型別
Number的具體規則,ES5規范中給了一個對應的結果表
| 型別 | 結果 |
|---|---|
| undefined | NaN |
| null | +0 |
| Boolean | NaN |
| undefined | 引數為true回傳1;false回傳+0 |
| Number | 回傳與之相等的值 |
| String | 有些復雜,舉例說明 |
| Object | 三步走戰略 |
- String: 空字串回傳0,出現任何一個非有效數字字符,回傳NaN
console.log(Number("1 3")) // NaN
console.log(Number("abc")) // NaN
console.log(Number("1a")) // NaN
console.log(Number("0x11")) // 17
console.log(Number("123")) // 123
console.log(Number("-123")) // -123
console.log(Number("1.2")) // 1.2
Object
參考第八問,最后對回傳的原始值執行Number方法
- 隱式:
number的隱式型別轉換比較復雜,對需要隱式轉換的部分執行Number:- 比較操作(
<, >, <=, >=) - 按位操作(
| & ^ ~) - 算數操作(
+ - * / %) 注意:+的運算元存在字串時,為string轉換 - 一元
+-操作
- 比較操作(
第十問:== 與 === 的區別
===是嚴格相等,要求資料型別和值都要相等;==只需要值相等,==會發生隱式型別轉換,===不會發生隱式型別轉換,==的轉換規則:
null, undefined在==下互相等且自身等
| 被比較數B | |||||
|---|---|---|---|---|---|
| Number | String | Boolean | Object | ||
| 比較數A | |||||
| Number | A === B | A === ToNumber(B) | A === ToNumber(B) | A == ToPrimitive(B) | |
| String | ToNumber(A) === B | ToNumber(A) === ToNumber(B) | ToNumber(A) === ToNumber(B) | ToPrimitive(B) == A | |
| Boolean | ToNumber(A) === B | ToNumber(A) === ToNumber(B) | ToNumber(A) === ToNumber(B) | ToNumber(A) == ToPrimitive(B) | |
| Object | ToPrimitive(A) == B | ToPrimitive(A) == B | ToPrimitive(A) == ToPrimitive(B) | A === B | |
第十一問:1 + {} 與 {} + 1的輸出結果分別是什么?
通過上面的學習,當物件與其他元素相加時,物件會發生隱式型別轉換,執行三步走戰略:
- 執行
toPrimitive,判斷物件是否有Symbol.toPrimitive,沒有該方法 - 執行
({}).toString(),回傳"[object Object]",回傳結果為原始值,轉換結束
此時 1 + {},一側為string型別,將1進行ToString()轉化為"1",最終輸出結果為 "1[object Object]"
注意: {} + 1輸出的結果會和1 + {}一樣嗎?
{}在JavaScript中,不止可以作為物件定義,也可以作為代碼塊的定義,js引擎會把{} + 1決議成1個代碼塊和1個+1,最終輸出結果為 1
答案
1[object Object]
1
常見習題
-
{} + {}無法決議成兩個代碼塊,兩個物件都經過三步走戰略,轉化為原始值
[object Object],最終結果[object Object][object Object] -
[] + {}陣列是特殊的物件,也需要三步走戰略,
arr.valueOf()仍是arr,所以需要繼續呼叫toString()方法,[]回傳"",最侄訓傳結果為[object Object] -
[] + []兩個arr經過三步走都回傳"",最終結果""
-
{} + []類似于
{} + 1,{} + []相當于{}; + [],一元+強制將""隱式轉換為0,最終結果為0
第十二問:Object.is() 與 === 區別
Object.is()來進行相等判斷時,一般情況下與 === 相同,它處理了一些特殊的情況,比如+0和 -0不再相等,兩個NaN是相等的,
第十三問:你能靈活運用parseInt 與 parseFloat嗎
parseInt:從數字類開始看,看到非數字類為止,回傳原來的數,(小數點也屬于非有效數字)
parseInt('123x') -> 123
parseInt('-023x') -> -23
parseInt('1.1') -> 1
parseInt('-abc') -> NaN
parseInt('x123') -> NaN
parseInt(string, radix)還有第二個引數radix表示要決議數字的基數,取值為2~36(默認值為10)parseFloat與parseInt類似,只不過它回傳浮點數,從數字類開始看,看到除了第一個點以外的非數字類為截止,回傳前面的數,
網紅題:[‘1’,‘2’,‘3’].map(parseInt)
這個網紅題考察的就是parseInt有兩個引數,map傳入的函式可執行三個引數:
// ele 遍歷的元素
// index 遍歷的元素索引
// arr 陣列
arr.map(function(ele, index, arr){})
[‘1’,‘2’,‘3’].map(parseInt)相當于執行了以下三次程序:
parseInt('1', 0, ['1','2','3'])
parseInt('2', 1, ['1','2','3'])
parseInt('3', 2, ['1','2','3'])
parseInt('1', 0, ['1','2','3']): radix為0時,默認取10,最后回傳1parseInt('2', 1, ['1','2','3']): radix取值為2~36,回傳NaNparseInt('3', 2, ['1','2','3']): radix取值為2,二進制只包括0,1,回傳NaN
第十四問:如何讓if(a == 1 && a == 2)條件成立?
valueOf的應用
var a = {
value: 0,
valueOf: function() {
this.value++;
return this.value;
}
};
console.log(a == 1 && a == 2);//true
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/321293.html
標籤:其他
