如今去作前端面试题都是心虚的, 原本能够作对的题,想一想好像有坑,而后答错了。举个例子: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 库,不妨也分享出来一块儿看看吧。