JavaScript 里的奇葩知識(shí)
授權(quán)轉(zhuǎn)載自:原罪?
https://segmentfault.com/a/1190000023941089
久經(jīng)沙場(chǎng)的前輩們,寫了無(wú)數(shù)代碼,踩了無(wú)數(shù)的坑。但有些坑,可能一輩子也踩不到摸不著,因?yàn)楦静粫?huì)發(fā)生在業(yè)務(wù)代碼里~~
1
Function.prototype 竟然是個(gè)函數(shù)類型。而自定義函數(shù)的原型卻是對(duì)象類型。
typeof?Function.prototype?===?'function';??//?true
function?People()?{}
typeof?People.prototype?===?'object';??????//?true
所以我們?cè)O(shè)置空函數(shù)可以這么做:
//?Good?
const?noop?=?Function.prototype;
//?Bad
const?noop?=?()?=>?{};
2
一個(gè)變量真的會(huì)不等于自身嗎?
const?x?=?NaN;
x?!==?x??//?true
這是目前為止 js 語(yǔ)言中唯一的一個(gè)不等于自己的數(shù)據(jù)。為什么?因?yàn)?NaN 代表的是一個(gè)范圍,而不是一個(gè)具體的數(shù)值。
在早期的 isNaN() 函數(shù)中,即使傳入字符串,也會(huì)返回 true ,這個(gè)問(wèn)題已經(jīng)在 es6 中修復(fù)。
isNaN('abc');???????//?true
Number.isNaN('abc')?//?false
所以如果您想兼容舊瀏覽器,用 x !== x 來(lái)判斷是不是NaN,是一個(gè)不錯(cuò)的方案。
3
構(gòu)造函數(shù)如果 return了新的數(shù)據(jù)
//?不返回
function?People()?{}
const?people?=?new?People();???//?People?{}
//?返回?cái)?shù)字
function?People()?{
??return?1;
}
const?people?=?new?People();???//?People?{}
//?返回新對(duì)象
function?Animal()?{
??return?{
????hello:?'world',
??};
}
const?animal?=?new?Animal();??//?{?hello:?'world'?}
在實(shí)例化構(gòu)造函數(shù)時(shí),返回非對(duì)象類型將不生效
4
.call.call 到底在為誰(shuí)瘋狂打call?
function?fn1()?{
??console.log(1);
}
function?fn2()?{
??console.log(2);
}
fn1.call.call(fn2);?//?2
所以 fn1.call.call(fn2) 等效于 fn2.call(undefined)。而且無(wú)論您加多少個(gè) .call,效果也是一樣的。
5
實(shí)例后的對(duì)象也能再次實(shí)例嗎?
function?People()?{}
const?lili?=?new?People();????????????//?People?{}
const?lucy?=?new?tom.constructor();???//?People?{}
因?yàn)?lili 的原型鏈指向了 People 的原型,所以通過(guò)向上尋找特性,最終在 Peopel.prototype 上找到了構(gòu)造器即 People 自身
6
setTimeout 嵌套會(huì)發(fā)生什么奇怪的事情?
console.log(0,?Date.now());
setTimeout(()?=>?{
??console.log(1,?Date.now());
??setTimeout(()?=>?{
????console.log(2,?Date.now());
????setTimeout(()?=>?{
??????console.log(3,?Date.now());
??????setTimeout(()?=>?{
????????console.log(4,?Date.now());
????????setTimeout(()?=>?{
??????????console.log(5,?Date.now());
??????????setTimeout(()?=>?{
????????????console.log(6,?Date.now());
??????????});
????????});
??????});
????});
??});
});
在0-4層,setTimeout 的間隔是 1ms ,而到第 5 層時(shí),間隔至少是 4ms 。
7
es6函數(shù)帶默認(rèn)參數(shù)時(shí)將生成聲明作用域
var?x?=?10;
function?fn(x?=?2,?y?=?function?()?{?return?x?+?1?})?{
??var?x?=?5;
??return?y();
}
fn();???//?3
8
函數(shù)表達(dá)式(非函數(shù)聲明)中的函數(shù)名不可覆蓋
const?c?=?function?CC()?{
??CC?=?123;
??return?CC;
};
c();?//?Function
當(dāng)然,如果設(shè)置 var CC = 123 ,加聲明關(guān)鍵詞是可以覆蓋的。
9
嚴(yán)格模式下,函數(shù)的 this 是 undefined 而不是 Window
//?非嚴(yán)格
function?fn1()?{
??return?this;
}
fn1();?//?Window
//?嚴(yán)格
function?fn2()?{
??'use?strict';
??return?this;
}
fn2();?//?undefined
對(duì)于模塊化的經(jīng)過(guò)webpack打包的代碼,基本都是嚴(yán)格模式的代碼。
10
取整操作也可以用按位操作
var?x?=?1.23?|?0;??//?1
因?yàn)榘次徊僮髦恢С?2位的整型,所以小數(shù)點(diǎn)部分全部都被拋棄
11
indexOf() 不需要再比較數(shù)字
const?arr?=?[1,?2,?3];
//?存在,等效于?>?-1
if?(~arr.indexOf(1))?{
}
//?不存在,等效于?===?-1
!~arr.indexOf(1);
按位操作效率高點(diǎn),代碼也簡(jiǎn)潔一些。也可以使用es6的 includes() 。但寫開源庫(kù)需要考慮兼容性的道友還是用 indexOf 比較好
12
getter/setter 也可以動(dòng)態(tài)設(shè)置嗎?
class?Hello?{
??_name?=?'lucy';
?
??getName()?{
????return?this._name;
??}
??
??//?靜態(tài)的getter
??get?id()?{
????return?1;
??}
}
const?hel?=?new?Hello();
hel.name;???????//?undefined
hel.getName();??//?lucy
//?動(dòng)態(tài)的getter
Hello.prototype.__defineGetter__('name',?function()?{
??return?this._name;
});
Hello.prototype.__defineSetter__('name',?function(value)?{
??this._name?=?value;
});
hel.name;???????//?lucy
hel.getName();??//?lucy
hel.name?=?'jimi';
hel.name;???????//?jimi
hel.getName();??//?jimi
13
0.3?-?0.2?!==?0.1??//?true
浮點(diǎn)操作不精確,老生常談了,不過(guò)可以接受誤差
0.3?-?0.2?-?0.1?<=?Number.EPSILON?//?true
14
class 語(yǔ)法糖到底是怎么繼承的?
function?Super()?{
??this.a?=?1;
}
function?Child()?{
??//?屬性繼承
??Super.call(this);
??this.b?=?2;
}
//?原型繼承
Child.prototype?=?new?Super();
const?child?=?new?Child();
child.a;??//?1
正式代碼的原型繼承,不會(huì)直接實(shí)例父類,而是實(shí)例一個(gè)空函數(shù),避免重復(fù)聲明動(dòng)態(tài)屬性
const?extends?=?(Child,?Super)?=>?{
??const?fn?=?function?()?{};
??
??fn.prototype?=?Super.prototype;
??Child.prototype?=?new?fn();
??Child.prototype.constructor?=?Child;
};
15
es6居然可以重復(fù)解構(gòu)對(duì)象
const?obj?=?{
??a:?{
????b:?1
??},
??c:?2
};
const?{?a:?{?b?},?a?}?=?obj;
一行代碼同時(shí)獲取 a 和 a.b 。
在a和b都要多次用到的情況下,普通人的邏輯就是先解構(gòu)出 a ,再在下一行解構(gòu)出 b 。
16
判斷代碼是否壓縮居然也這么秀
function?CustomFn()?{}
const?isCrashed?=?typeof?CustomFn.name?===?'string'?&&?CustomFn.name?===?'CustomFn';
17
對(duì)象 === 比較的是內(nèi)存地址,而 >= 將比較轉(zhuǎn)換后的值
{}?===?{}?//?false
//?隱式轉(zhuǎn)換?toString()
{}?>=?{}??//?true
18
intanceof 的判斷方式是原型是否在當(dāng)前對(duì)象的原型鏈上面
function?People()?{}
function?Man()?{}
Man.prototype?=?new?People();
Man.prototype.constructor?=?Man;
const?man?=?new?Man();
man?instanceof?People;????//?true
//?替換People的原型
People.prototype?=?{};
man?instanceof?People;????//?false
如果您用es6的class的話,prototype原型是不允許被重新定義的,所以不會(huì)出現(xiàn)上述情況
19
Object.prototype.__proto__?===?null;?//?true
這是原型鏈向上查找的最頂層,一個(gè) null
20
parseInt 太小的數(shù)字會(huì)產(chǎn)生 bug
parseInt(0.00000000454);??//?4
parseInt(10.23);??????????//?10
21
1?+?null??????????//?1
1?+?undefined?????//?NaN
Number(null)??????//?0
Number(undefined)?//?NaN
22
arguments 和形參是別名關(guān)系
function?test(a,?b)?{
??console.log(a,?b);?//?2,?3
??
??arguments[0]?=?100;
??arguments[1]?=?200;
??
??console.log(a,?b);?//?100,?200
}
test(2,?3);
但是您可以用 use strict 嚴(yán)格模式來(lái)避免這一行為,這樣 arguments 就只是個(gè)副本了。
23
void 是個(gè)固執(zhí)的老頭
void?0?===?undefined??????????//?true
void?1?===?undefined??????????//?true
void?{}?===?undefined?????????//?true
void?'hello'?===?undefined????//?true
void?void?0?===?undefined?????//?true
跟誰(shuí)都不沾親~~
24
try/catch/finally 也有特定的執(zhí)行順序
function?fn1()?{
??console.log('fn1');
??return?1;
}
function?fn2()?{
??console.log('fn2');
??return?2;
}
function?getData()?{
??try?{
????throw?new?Error('');
??}?catch?(e)?{
????return?fn1();
??}?finally?{
????return?fn2();
??}
}
console.log(getData());
//?打印順序:?'fn1',?'fn2',?2
在 try/catch 代碼塊中,如果碰到 return xxyyzz; 關(guān)鍵詞,那么 xxyyzz 會(huì)先執(zhí)行并把值放在臨時(shí)變量里,接著去執(zhí)行 finally 代碼塊的內(nèi)容后再返回該臨時(shí)變量。
如果 finally 中也有 return aabbcc ,那么會(huì)立即返回新的數(shù)據(jù) aabbcc 。
25
是否存在這樣的變量 x ,使得它等于多個(gè)數(shù)字?
const?x?=?{
??value:?0,
??toString()?{
????return?++this.value;
??}
}
x?==?1?&&?x?==?2?&&?x?==?3;????//?true
通過(guò)隱式轉(zhuǎn)換,這樣不是什么難的事情。
26
clearTimeout 和 clearInterval 可以互換~~~~使用嗎
var?timeout?=?setTimeout(()?=>?console.log(1),?1000);
var?interval?=?setInterval(()?=>?console.log(2),?800);
clearInterval(timeout);
clearTimeout(interval);
答案是:YES 。大部分瀏覽器都支持互相清理定時(shí)器,但是建議使用對(duì)應(yīng)的清理函數(shù)。
27
下面的打印順序是?
setTimeout(()?=>?{
??console.log(1);
},?0);
new?Promise((resolve)?=>?{
??console.log(2);
??resolve();
}).then(()?=>?console.log(3));
function?callMe()?{
??console.log(4);
}
(async?()?=>?{
??await?callMe();
??console.log(5);
})();
答案是:2, 4, 3, 5, 1
主線任務(wù):2,4?
28
null 是 object 類型,但又不是繼承于 Object ,它更像一個(gè)歷史遺留的 bug 。鑒于太多人在用這個(gè)特性,修復(fù)它反而會(huì)導(dǎo)致成千上萬(wàn)的程序出錯(cuò)。
typeof?null?===?'object';??????????????//?true
Object.prototype.toString.call(null);??//?[object?Null]
null?instanceof?Object;????????????????//?false
腦袋空了,想到再加。。。

我是依揚(yáng),螞蟻集團(tuán)-保險(xiǎn)團(tuán)隊(duì)正在大量招聘中,詳情見(jiàn):我們是螞蟻保險(xiǎn)前端團(tuán)隊(duì),我們今年在做什么,有興趣快來(lái)聯(lián)系我吧[email protected]
