摘要:這種情況,它們返回一個(gè)布爾型值。語(yǔ)法描述邏輯非如果能轉(zhuǎn)換為,返回如果能轉(zhuǎn)換為,則返回。轉(zhuǎn)中能夠轉(zhuǎn)換為的字面量是可枚舉的,包含空字符串。
博客 github 地址: https://github.com/HCThink/h-blog/blob/master/interesting/in5.md
github 首頁(yè)(star+watch,一手動(dòng)態(tài)直達(dá)): https://github.com/HCThink/h-blog
掘金 link , 掘金 專(zhuān)欄
segmentfault 主頁(yè)
原創(chuàng)禁止私自轉(zhuǎn)載
廣告部門(mén)長(zhǎng)期招收大量研發(fā)崗位【前端,后端,算法】,歡迎各位大神投遞,踴躍嘗試。
坐標(biāo): 頭條,大量招人,難度有降低,大多能拿到很不錯(cuò)的漲幅,未上市,offer 給力!歡迎騷擾郵箱!
戳我: 戳我: hooper.echo@gmail.com
[] == ![] ?應(yīng)該是騰訊面試題, 原題更加復(fù)雜
面試遇到這種令人頭皮發(fā)麻的題,該怎么辦呢? 不要慌,我們科學(xué)的應(yīng)對(duì)即可。
經(jīng)驗(yàn)法,簡(jiǎn)稱(chēng)瞎蒙對(duì)于簡(jiǎn)短而罕見(jiàn)的寫(xiě)法,最好的方法就是經(jīng)驗(yàn)法,基本原則就是瞎蒙,雖然聽(tīng)著有點(diǎn)扯淡,實(shí)際上這不失為一個(gè)好辦法,對(duì)于一個(gè)比較陌生的問(wèn)題,我們通過(guò)經(jīng)驗(yàn)瞎幾把猜一個(gè)「大眾」答案:
簡(jiǎn)單觀察此題,我們發(fā)現(xiàn)題目想讓一個(gè) 數(shù)組和他的 非 作比較, 從正常的思維來(lái)看,一個(gè)數(shù)和他的非,應(yīng)該是不相等的。
所以我們 first An is : false反向操作法
然而你看著面試官淫邪的笑容,突然意識(shí)到,問(wèn)題并不簡(jiǎn)單,畢竟這家公司還可以,不會(huì)來(lái)這么小兒科的問(wèn)題吧。再轉(zhuǎn)念一想,這 tm 的是 js 啊,畢竟 js 經(jīng)常不按套路出牌啊。
于是你又大膽做出了一個(gè)假設(shè): [] == ![] 是 true!
大致結(jié)論有了, 那該怎么推導(dǎo)這個(gè)結(jié)論呢?我們逐步分解一下這個(gè)問(wèn)題。分而治之
最終結(jié)論后面分析很長(zhǎng),涉及到大篇幅的 ECMAScript 規(guī)范的解讀,冗長(zhǎng)而枯燥,不想看的同學(xué),可以在這里直接拿到結(jié)論
[] == ![] -> [] == false -> [] == 0 -> [].valueOf() == 0 -> [].toString() == 0 -> "" == 0 -> 0 == 0 -> true分析
如果你決定要看,千萬(wàn)堅(jiān)持看完,三十分鐘之后我一定會(huì)給你一個(gè)驚喜。
這是個(gè)奇怪的問(wèn)題,乍一看形式上有些怪異, 如果面試中你遇到這么個(gè)題,應(yīng)該會(huì)有些惱火:這 tm 什么玩意?! shift!(防和諧梗)。
雖然有點(diǎn)懵,不過(guò)還是理性的分析一下,既然這個(gè)表達(dá)式含有多個(gè)運(yùn)算符, 那首先還是得看看運(yùn)算符優(yōu)先級(jí)。
運(yùn)算符優(yōu)先級(jí)運(yùn)算符優(yōu)先級(jí)表
而此題中出現(xiàn)了兩個(gè)操作符: 「!」, 「==」, 查表可知, 邏輯非優(yōu)先級(jí)是 16, 而等號(hào)優(yōu)先級(jí)是 10, 可見(jiàn)先執(zhí)行 ![] 操作。在此之前我們先看看 邏輯非
邏輯非 !mozilla 邏輯非: !
邏輯運(yùn)算符通常用于Boolean型(邏輯)值。這種情況,它們返回一個(gè)布爾型值。
語(yǔ)法描述: 邏輯非(!) !expr
如果expr能轉(zhuǎn)換為true,返回false;
如果expr能轉(zhuǎn)換為false,則返回true。
轉(zhuǎn) booljs 中能夠轉(zhuǎn)換為false的字面量是可枚舉的,包含
null;
NaN;
0;
空字符串("");
undefined。
所以 ![] => false
于是乎我們將問(wèn)題轉(zhuǎn)化為: [] == false== 運(yùn)算符
這是個(gè)勁爆的操作符,正經(jīng)功能沒(méi)有,自帶隱式類(lèi)型轉(zhuǎn)換經(jīng)常令人對(duì) js 刮目相看, 實(shí)際上現(xiàn)在網(wǎng)上也沒(méi)有對(duì)這個(gè)操作符轉(zhuǎn)換規(guī)則描述比較好的,這個(gè)時(shí)候我們就需要去 ECMAscript 上去找找標(biāo)準(zhǔn)了。
ECMAScript? 2019 : 7.2.14 Abstract Equality Comparison
規(guī)范描述: The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:
If Type(x) is the same as Type(y), then
Return the result of performing Strict Equality Comparison x === y.
If x is null and y is undefined, return true.
If x is undefined and y is null, return true.
If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
If Type(x) is Object and Type(y) is either String, Number, or Symbol, return the result of the comparison ToPrimitive(x) == y.
Return false.
依據(jù)規(guī)范 6, 7 可知,存在 bool 則會(huì)將自身 ToNumber 轉(zhuǎn)換 !ToNumber(x) 參考 花絮下的 !ToNumber, 主要是講解 !的意思 ! 前綴在最新規(guī)范中表示某個(gè)過(guò)程會(huì)按照既定的規(guī)則和預(yù)期的執(zhí)行【必定會(huì)返回一個(gè) number 類(lèi)型的值,不會(huì)是其他類(lèi)型,甚至 throw error】
得到: [] == !ToNumber(false)
ToNumberECMAScript? 2019 : 7.1.3ToNumber
If argument is true, return 1. If argument is false, return +0.
可知: !ToNumber(false) => 0; [] == 0
然后依據(jù)規(guī)范 8 9, 執(zhí)行 ToPrimitive([])
ToPrimitiveECMAScript? 2019 : 7.1.1ToPrimitive ( input [ , PreferredType ] )
The abstract operation ToPrimitive converts its input argument to a non-Object type. [嘗試轉(zhuǎn)換為原始對(duì)象]
If an object is capable of converting to more than one primitive type, it may use the optional hint PreferredType to favour that type. Conversion occurs according to the following algorithm. [如果一個(gè)對(duì)象可以被轉(zhuǎn)換為多種原語(yǔ)類(lèi)型, 則參考 PreferredType, 依據(jù)如下規(guī)則轉(zhuǎn)換]
Assert: input is an ECMAScript language value.
If Type(input) is Object, then
If PreferredType is not present, let hint be "default".
Else if PreferredType is hint String, let hint be "string".
Else PreferredType is hint Number, let hint be "number".
Let exoticToPrim be ? GetMethod(input, @@toPrimitive).
If exoticToPrim is not undefined, then
Let result be ? Call(exoticToPrim, input, ? hint ?).
If Type(result) is not Object, return result.
Throw a TypeError exception.
If hint is "default", set hint to "number".
Return ? OrdinaryToPrimitive(input, hint).
Return input.
大致步驟就是 確定 PreferredType 值[If hint is "default", set hint to "number".], 然后調(diào)用 GetMethod, 正常情況下 GetMethod 返回 GetV, GetV 將每個(gè)屬性值 ToObject, 然后返回 O.[[Get]](P, V).
Assert: IsPropertyKey(P) is true.
Let O be ? ToObject(V).
Return ? O.[[Get]](P, V).
[[Get]]ECMAScript? 2019 : 9.1.8[[Get]] ( P, Receiver )
Return the value of the property whose key is propertyKey from this object[檢索對(duì)象的 propertyKey 屬性值]
然后 ToPrimitive step 7 返回 OrdinaryToPrimitive(input, hint)
OrdinaryToPrimitive( O, hint )ECMAScript? 2019 : 7.1.1.1OrdinaryToPrimitive ( O, hint )
Assert: Type(O) is Object.
Assert: Type(hint) is String and its value is either "string" or "number".
If hint is "string", then
Let methodNames be ? "toString", "valueOf" ?.
Else,
Let methodNames be ? "valueOf", "toString" ?.
For each name in methodNames in List order, do
5.1 Let method be ? Get(O, name).
5.2 If IsCallable(method) is true, then
5.2.1 Let result be ? Call(method, O).
5.2.2 If Type(result) is not Object, return result.
Throw a TypeError exception.
上述過(guò)程說(shuō)的很明白: 如果 hint is String,并且他的 value 是 string 或者 number【ToPrimitive 中給 hint 打的標(biāo)簽】,接下來(lái)的處理邏輯,3,4 步描述的已經(jīng)很清楚了。
步驟 5,則是依次處理放入 methodNames 的操作[這也解答了我一直以來(lái)的一個(gè)疑問(wèn),網(wǎng)上也有說(shuō)對(duì)象轉(zhuǎn) string 的時(shí)候,是調(diào)用 tostring 和 valueof, 但是總是含糊其辭,哪個(gè)先調(diào)用,哪個(gè)后調(diào)用,以及是不是兩個(gè)方法都會(huì)調(diào)用等問(wèn)題總是模棱兩可,一句帶過(guò) /手動(dòng)狗頭]。
推論該了解的基本上都梳理出來(lái)了, 說(shuō)實(shí)話,非常累,壓著沒(méi)有每個(gè)名詞都去發(fā)散。不過(guò)大致需要的環(huán)節(jié)都有了.
我們回過(guò)頭來(lái)看這個(gè)問(wèn)題: 在對(duì) == 操作符描述的步驟 8 9中,調(diào)用 ToPrimitive(y) 可見(jiàn)沒(méi)指定 PreferredType, 因此 hint 是 default,也就是 number【參考: 7.1.1ToPrimitive 的步驟2-f】
接著調(diào)用 OrdinaryToPrimitive(o, number) 則進(jìn)入 7.1.1.1OrdinaryToPrimitive 的步驟 4 ,然后進(jìn)入步驟 5 先調(diào)用 valueOf,步驟 5.2.2 描述中如果返回的不是 Object 則直接返回,否則才會(huì)調(diào)用 toString。
所以 [] == 0 => [].valueOf()[.toString()] == 0. 我們接著來(lái)看 數(shù)組的 valueOf 方法, 請(qǐng)注意區(qū)分一點(diǎn),js 里內(nèi)置對(duì)象都繼承的到 valueOf 操作,但是部分對(duì)象做了覆寫(xiě), 比如 String.prototype.valueOf,所以去看看 Array.prototype.valueOf 有沒(méi)有覆寫(xiě)。
結(jié)果是沒(méi)有,啪啪打臉啊,尼瑪,于是乎我們看 Object.prototype.valueOf
Array.prototype.valueOf from Object.prototype.valueOfECMAScript? 2019 : 19.1.3.7Object.prototype.valueOf ( )
When the valueOf method is called, the following steps are taken:
Return ? ToObject(this value).
This function is the %ObjProto_valueOf% intrinsic object.
我們接著看 ToObject【抓狂,但是要堅(jiān)持】。
ToObjectECMAScript? 2019 : 7.1.13ToObject ( argument )
Object : Return argument?! 這步算是白走了。我們接著看 toString,同樣的我們要考慮覆寫(xiě)的問(wèn)題。
Array.prototype.toString()ECMAScript? 2019 : 22.1.3.28Array.prototype.toString ( )
Let array be ? ToObject(this value).
Let func be ? Get(array, "join").
If IsCallable(func) is false, set func to the intrinsic function %ObjProto_toString%.
Return ? Call(func, array).
可見(jiàn)調(diào)用了 join 方法【ps: 這里面還有個(gè)小故事,我曾經(jīng)去滴滴面試,二面和我聊到這個(gè)問(wèn)題,我說(shuō)數(shù)組的 toString 調(diào)用了 join ,面試官給我說(shuō),你不要看著調(diào)用結(jié)果就臆測(cè)內(nèi)部實(shí)現(xiàn),不是這樣思考問(wèn)題的...... 我就搖了搖頭,結(jié)果止步二面,獵頭反饋的拒絕三連: 方向不匹配,不適合我們,滾吧。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/99965.html
摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠?lái)都是中的主導(dǎo)范式。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。 JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠?lái)都是JavaScript中的主導(dǎo)范式。JavaScript作為一門(mén)多范式編程語(yǔ)言,然而,近幾年,函數(shù)式編程越來(lái)越多得受到開(kāi)發(fā)者的青睞。函數(shù)式編程是一種強(qiáng)調(diào)減少對(duì)程序外部狀態(tài)產(chǎn)生改變的方式。因此,...
摘要:本文總結(jié)了前端老司機(jī)經(jīng)常問(wèn)題的一些問(wèn)題并結(jié)合個(gè)人總結(jié)給出了比較詳盡的答案。網(wǎng)易阿里騰訊校招社招必備知識(shí)點(diǎn)。此外還有網(wǎng)絡(luò)線程,定時(shí)器任務(wù)線程,文件系統(tǒng)處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機(jī)制叫做事件循環(huán)。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結(jié)了前端老司機(jī)經(jīng)常問(wèn)題的一些問(wèn)題并結(jié)合個(gè)...
摘要:本文總結(jié)了前端老司機(jī)經(jīng)常問(wèn)題的一些問(wèn)題并結(jié)合個(gè)人總結(jié)給出了比較詳盡的答案。網(wǎng)易阿里騰訊校招社招必備知識(shí)點(diǎn)。此外還有網(wǎng)絡(luò)線程,定時(shí)器任務(wù)線程,文件系統(tǒng)處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機(jī)制叫做事件循環(huán)。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結(jié)了前端老司機(jī)經(jīng)常問(wèn)題的一些問(wèn)題并結(jié)合個(gè)...
摘要:與此相對(duì),強(qiáng)類(lèi)型語(yǔ)言的類(lèi)型之間不一定有隱式轉(zhuǎn)換。三為什么是弱類(lèi)型弱類(lèi)型相對(duì)于強(qiáng)類(lèi)型來(lái)說(shuō)類(lèi)型檢查更不嚴(yán)格,比如說(shuō)允許變量類(lèi)型的隱式轉(zhuǎn)換,允許強(qiáng)制類(lèi)型轉(zhuǎn)換等等。在中,加性運(yùn)算符有大量的特殊行為。 從++[[]][+[]]+[+[]]==10?深入淺出弱類(lèi)型JS的隱式轉(zhuǎn)換 本文純屬原創(chuàng)? 如有雷同? 純屬抄襲? 不甚榮幸! 歡迎轉(zhuǎn)載! 原文收錄在【我的GitHub博客】,覺(jué)得本文寫(xiě)的不算爛的...
摘要:插件開(kāi)發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實(shí)現(xiàn)文件分片斷點(diǎn)續(xù)傳。 Vue.js 插件開(kāi)發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強(qiáng)大而且簡(jiǎn)單的方式。插....
閱讀 2289·2021-11-24 09:38
閱讀 1986·2021-11-22 14:44
閱讀 1150·2021-07-29 13:48
閱讀 2615·2019-08-29 13:20
閱讀 1115·2019-08-29 11:08
閱讀 2046·2019-08-26 10:58
閱讀 1264·2019-08-26 10:55
閱讀 3149·2019-08-26 10:39