JS 數據類型方面的蹊蹺

如今去作前端面試題都是心虛的, 原本能夠作對的題,想一想好像有坑,而後答錯了。舉個例子:javascript

Number([0]);           // 0
[0] == true;           // false
if ([0]) alert('ok');  // "ok" // 恩? 不都是 false 嗎
複製代碼

因此本文將盡量多的去試圖挖一下 javascript 數據類型方面的蹊蹺。前端

數據相等

等於問題

這張圖大夥應該很熟悉了,但其實這裏面有些很詭異的問題,很迷很迷。java

0 == '0';   // true
0 == [];    // true
'0' == [];  // false
複製代碼

雙等時也許是進行了類型轉換的, 好比都轉爲數字或字符串後再進行的比較。git

我的猜想轉換的順序 可能 以下:es6

undefined  < null < Boolean < Number < String < Array
複製代碼

它是一層層向下進行轉換後進行比較的。github

'0' == true  // false
// 實則是 0 == true 的比較
複製代碼

再好比web

Boolean([0]);  // true
[0] == true;   // false
// 實際是 '0' == true 最後 0 == true 的比較
複製代碼

<= 這類數值判斷,也是相似的,但很快就發現, 以上猜想並不完善,還有更多一步前置的 Number 轉換。面試

2 > true;      // true
'1' > '2';     // false
複製代碼
undefined == undefined;  // true
undefined <= undefined;  // false
// 由於 Number(undefined) 的結果是 NaN
複製代碼

注意 [2] == [2] 固然是爲 false 啦, 這個可 別混淆 了,覺得也要去轉化。數組

此處稍微提一下 -0 的存在,會形成 Infinity-Infinity 的不一樣。 但咱們多半會作分母不爲零的判斷的,恩大概會的吧。函數

0 === -0;           // true
(1/-0) === (1/0);   // false
複製代碼

數據類型判斷

if 判斷

通常使用 if 大體會有如下五種狀況,三目判斷並或非 也包含其中。

if (a <= b)
if (a) 
if (a())
if (a = 1)
if (!a)
複製代碼

if 判斷
如圖所示,if 中結果便是 Boolean() 轉化後的結果。

請再回味一番,切實記住 if 判斷與等於判斷的不一樣喲。

還覺得 !a 的判斷會有坑,試驗下來舒了口氣,並無什麼特別之處。

typeof 判斷

這章好像要記住的和留意的東西也並很少,

typeof [] === 'object';
typeof NaN === 'number'
typeof null === 'object'
複製代碼

卻也是判斷中稍有點難判的,因此纔出現了 Array.isArrayisNaN 這樣的方法存在。 爲啥我試不出 typeof 爲 array 的狀況呀,很奇怪耶,是我記錯了咩

還有像 Date RegExp arguments 等天然就是對象了,typeof 的坑相對要少不少。

instanceof 判斷

[] instanceof Array 判數組真的很方便,但這塊也仍是有坑的。

'a' instanceof String                // false
(new String('a')) instanceof String  // true
複製代碼

除此以外,還有原型鏈上的一點問題:

function Foo(){} 
var foo = new Foo(); 
console.log(foo instanceof Foo);  //true

Foo.prototype = new Aoo();
var foo2 = new Foo(); 
console.log(foo2 instanceof Foo)  // true 
console.log(foo2 instanceof Aoo)  // true
複製代碼

說實話,除了幾個特例,用這個來判原型其實並非很好的方法。 參考:www.ibm.com/developerwo…

constructor 判斷

constructor 相比 instanceof 有一點優點,就是它不隨 __proto__ 的改變

function A(){};
var a = new A();
var b = new A();
a.__proto__ = {};

a instanceof A       // false
b.constructor === A  // true
複製代碼

以往 es5 作繼承時還要本身給 A.prototype.constructor 設新值, 有了 es6 的 class 後已經很簡單了,用 constructor 來判原型也穩妥了起來。 至於基礎數據類型嘛,也不太推薦用此方法。

is 方法判斷

isFinite();
isNaN();
Number.isNaN();
Array.isArray();
複製代碼

其餘判斷

Object.is() 判斷

其實 Object.is() 是相似 === 的,但又有點不同,它是真真正正的絕對相等。

+0 === -0           // true
Object.is(+0, -0)   // false

NaN === NaN          // false
Object.is(NaN, NaN)  // true
複製代碼

key in object 判斷

還需稍微分清一下原型與實例便可,即 for-infor-of 的區別。

'0' in [1, 2];          // true
'now' in Date;          // true
'getFullYear' in Date;  // false
複製代碼

至於項目是使用如下哪一種判斷就見仁見智了。

if (Array.prototype.includes) {}
'includes' in [];
複製代碼

prototype 判斷

obj.hasOwnProperty(key)obj.isPrototypeOf(obj2) 等相關方法,整理中

強制數據類型轉換

運算式自動轉換

+' 014'   // 14
+'0x12'   // 18

1 + '14'    // '114'
1 + '0x12'  // '10x12'
1 + +'14'   // 15
'14' + 1    // '141'

1 + [1, 1];     // '11,1'
1 + {};         // '1[object Object]'

1 + null;       // 1
1  +undefined;  // NaN
複製代碼

很鮮明,當有單獨的運算符存在時(單加括號是不行滴), 會幫忙 Number 轉換,不然 String 轉換。 還請注意上例中後 4 種特殊的狀況。

進行 ++ 運算時並不會幫忙轉換爲數字,還容易報錯。 因此使用時這裏得留個心眼喲。

++'14'    // ReferenceError
複製代碼

還有兩個特立獨行的數字運算,即 Infinity0 的正負號。

Infinity+Infinity;      // Infinity
-Infinity+(-Infinity);  // -Infinity
Infinity+(-Infinity);   // NaN

+0+(+0);     // 0
(-0)+(-0);   // -0
(+0)+(-0);   // 0
複製代碼

再看一個絕對不會遇到的特例, {} + [] 理論上應該是 '[object Object]' + '' 纔對, 就算不是也應該是 NaN + 0 吧,結果我又猜錯了。 遇事不決問百度,結果震驚了,這裏的 {} 被當成空代碼塊了,+[] 天然就是 0 了。

[] + {}; // '[object Object]'
{} + []; // 0
複製代碼

對象式轉換

對象式轉換一覽

Number() 轉換

NumberparseInt 的不一樣,將於下文 parseInt 系列方法 講述

String() 轉換

探討一下 StringtoString 的不一樣吧。

一方面是部分數據類型沒有 toString 方法:

String(null);        // 'null'
(null).toString();        // Uncaught TypeError
(undefined).toString();   // Uncaught TypeError
複製代碼

另外一方面是 toString 能夠傳個進制數的參(僅對數字類型有用

(30).toString(16);    // "1e"
('30').toString(16);  // "30"
複製代碼

至於 Date Error RegRxp 的字符串化,基本不會出啥幺蛾子。

用原型的 toString 來判數據類型也是種很巧妙經常使用的方法。

function typeOf(obj) {
  var typeStr = Object.prototype.toString.call(obj).split(" ")[1];
  return typeStr.substr(0, typeStr.length - 1).toLowerCase();
}
typeOf([]);    // 'array'
複製代碼

函數式轉換

原型方法

toString 在上文已有介紹,但還得再區分一下數組的。

[1,[2,"abc","",0,null,undefined,false,NaN],3].toString();
// "1,2,abc,,0,,,false,NaN,3"
複製代碼

也便是下例想表達的意思:

(null).toString();   // Uncaught TypeError
[null].toString();   // ''
複製代碼

toStringvalueOf 大體是相同的,可是否有不一樣,整理中...

再則 (1).toFixed Date.parse 等,應該不會有啥常見錯誤。 只需注意那些是會 對入參進行隱形轉換 的,下文 參數的隱形轉換 將介紹

parseInt 系列方法

window.parseIntNumber.parseInt 是全等的,即徹底相同。

主要來看 NumberparseInt 的不一樣,挺迷的, 它們並非單純的數據類型轉化那麼簡單,舉個例子:

Number('');     // 0
parseInt('');   // NaN
複製代碼

parseInt 就很花哨,還會再多進行一些暗箱操做來判斷和適配成數字。 可見,用 Number 轉非整數時會是更好的選擇。

parseInt(' 10 ');   // 10 // 自動去空格,通用
parseInt('10.2');   // 10 // 數字後的全剔除,Number 和 parseFloat 沒問題
parseInt('1e2');    // 1 // 區分不出科學計數法,Number 和 parseFloat 沒問題
parseFloat('0x5');  // 0 // 區分不出進制,Number 和 parseInt 沒問題
複製代碼

當參數爲數組時,固然也是先轉 String 的咯, 而 parseInt 又能去除 , 後的字符,因此就有下面的狀況。

Number([1, 2]);    // NaN
parseInt([1, 2]);  // 1
複製代碼

參數的隱形轉換

比較典型的 isNaN 是先用 Number 轉了一次,但 Number.isNaN 就沒有。

isNaN('1x');          // true
Number.isNaN('1x');   // false
複製代碼

這方面沒作什麼整理,遇到了再補吧。

'12'.replace(1, '');    // "2"
複製代碼

其餘強行轉換

JSON.stringify()

JSON.parse(JSON.strigify()) 深拷貝時可得注意了喲。 其實遞歸加對象解構來作深拷貝要更好一些喲。

JSON.stringify(Infinity);   // 'null'
JSON.stringify(NaN);        // 'null'
JSON.stringify(undefined);        // undefined
JSON.stringify({a: undefined});   // '{}'
JSON.stringify({a: null});        // '{"a":null}'
JSON.stringify(() => {});         // 'undefined'
複製代碼
encode 系列

encodeURI 方法不會對下列字符編碼 ASCII字母、數字、~!@#$&*()=:/,;?+'

encodeURIComponent 方法不會對下列字符編碼 ASCII字母、數字、~!*()'

因此 encodeURIComponentencodeURI 編碼的範圍更大。

其餘
Array.from('foo');          // ["f", "o", "o"]
Object.assign([1], [2,3]);  // [2, 3]
複製代碼

大體就是這些了,寫完要自閉一會,整個過程充滿了懷疑與揣測。 雖然作了較爲系統的拆分,但仍是得認可沒寫好,敬請期待後續吧。

我還有一個 BUG 庫,不妨也分享出來一塊兒看看吧。

相關文章
相關標籤/搜索