如今去作前端面試題都是心虛的, 原本能夠作對的題,想一想好像有坑,而後答錯了。舉個例子: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 (a <= b)
if (a)
if (a())
if (a = 1)
if (!a)
複製代碼
Boolean()
轉化後的結果。
請再回味一番,切實記住 if 判斷與等於判斷的不一樣喲。
還覺得 !a
的判斷會有坑,試驗下來舒了口氣,並無什麼特別之處。
這章好像要記住的和留意的東西也並很少,
typeof [] === 'object';
typeof NaN === 'number'
typeof null === 'object'
複製代碼
卻也是判斷中稍有點難判的,因此纔出現了 Array.isArray
和 isNaN
這樣的方法存在。 爲啥我試不出 typeof 爲 array 的狀況呀,很奇怪耶,是我記錯了咩
還有像 Date
RegExp
arguments
等天然就是對象了,typeof
的坑相對要少不少。
用 [] 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
相比 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
來判原型也穩妥了起來。 至於基礎數據類型嘛,也不太推薦用此方法。
isFinite();
isNaN();
Number.isNaN();
Array.isArray();
複製代碼
其實 Object.is() 是相似 === 的,但又有點不同,它是真真正正的絕對相等。
+0 === -0 // true
Object.is(+0, -0) // false
NaN === NaN // false
Object.is(NaN, NaN) // true
複製代碼
還需稍微分清一下原型與實例便可,即 for-in
和 for-of
的區別。
'0' in [1, 2]; // true
'now' in Date; // true
'getFullYear' in Date; // false
複製代碼
至於項目是使用如下哪一種判斷就見仁見智了。
if (Array.prototype.includes) {}
'includes' in [];
複製代碼
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
複製代碼
還有兩個特立獨行的數字運算,即 Infinity
和 0
的正負號。
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
與 parseInt
的不一樣,將於下文 parseInt 系列方法 講述
探討一下 String
和 toString
的不一樣吧。
一方面是部分數據類型沒有 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(); // ''
複製代碼
toString
與 valueOf
大體是相同的,可是否有不一樣,整理中...
再則 (1).toFixed
Date.parse
等,應該不會有啥常見錯誤。 只需注意那些是會 對入參進行隱形轉換 的,下文 參數的隱形轉換 將介紹
window.parseInt
和 Number.parseInt
是全等的,即徹底相同。
主要來看 Number
與 parseInt
的不一樣,挺迷的, 它們並非單純的數據類型轉化那麼簡單,舉個例子:
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.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'
複製代碼
encodeURI
方法不會對下列字符編碼 ASCII字母、數字、~!@#$&*()=:/,;?+'
encodeURIComponent
方法不會對下列字符編碼 ASCII字母、數字、~!*()'
因此 encodeURIComponent
比 encodeURI
編碼的範圍更大。
Array.from('foo'); // ["f", "o", "o"]
Object.assign([1], [2,3]); // [2, 3]
複製代碼
大體就是這些了,寫完要自閉一會,整個過程充滿了懷疑與揣測。 雖然作了較爲系統的拆分,但仍是得認可沒寫好,敬請期待後續吧。
我還有一個 BUG 庫,不妨也分享出來一塊兒看看吧。