摘要:這種情況,它們返回一個布爾型值。語法描述邏輯非如果能轉(zhuǎn)換為,返回如果能轉(zhuǎn)換為,則返回。轉(zhuǎn)中能夠轉(zhuǎn)換為的字面量是可枚舉的,包含空字符串。
博客 github 地址: https://github.com/HCThink/h-blog/blob/master/interesting/in5.md
github 首頁(star+watch,一手動態(tài)直達): https://github.com/HCThink/h-blog
掘金 link , 掘金 專欄
segmentfault 主頁
原創(chuàng)禁止私自轉(zhuǎn)載
廣告部門長期招收大量研發(fā)崗位【前端,后端,算法】,歡迎各位大神投遞,踴躍嘗試。
坐標(biāo): 頭條,大量招人,難度有降低,大多能拿到很不錯的漲幅,未上市,offer 給力!歡迎騷擾郵箱!
戳我: 戳我: [email protected]
[] == ![] ?應(yīng)該是騰訊面試題, 原題更加復(fù)雜
面試遇到這種令人頭皮發(fā)麻的題,該怎么辦呢? 不要慌,我們科學(xué)的應(yīng)對即可。
經(jīng)驗法,簡稱瞎蒙對于簡短而罕見的寫法,最好的方法就是經(jīng)驗法,基本原則就是瞎蒙,雖然聽著有點扯淡,實際上這不失為一個好辦法,對于一個比較陌生的問題,我們通過經(jīng)驗瞎幾把猜一個「大眾」答案:
簡單觀察此題,我們發(fā)現(xiàn)題目想讓一個 數(shù)組和他的 非 作比較, 從正常的思維來看,一個數(shù)和他的非,應(yīng)該是不相等的。
所以我們 first An is : false反向操作法
然而你看著面試官淫邪的笑容,突然意識到,問題并不簡單,畢竟這家公司還可以,不會來這么小兒科的問題吧。再轉(zhuǎn)念一想,這 tm 的是 js 啊,畢竟 js 經(jīng)常不按套路出牌啊。
于是你又大膽做出了一個假設(shè): [] == ![] 是 true!
大致結(jié)論有了, 那該怎么推導(dǎo)這個結(jié)論呢?我們逐步分解一下這個問題。分而治之
最終結(jié)論后面分析很長,涉及到大篇幅的 ECMAScript 規(guī)范的解讀,冗長而枯燥,不想看的同學(xué),可以在這里直接拿到結(jié)論
[] == ![] -> [] == false -> [] == 0 -> [].valueOf() == 0 -> [].toString() == 0 -> "" == 0 -> 0 == 0 -> true分析
如果你決定要看,千萬堅持看完,三十分鐘之后我一定會給你一個驚喜。
這是個奇怪的問題,乍一看形式上有些怪異, 如果面試中你遇到這么個題,應(yīng)該會有些惱火:這 tm 什么玩意?! shift?。ǚ篮椭C梗)。
雖然有點懵,不過還是理性的分析一下,既然這個表達式含有多個運算符, 那首先還是得看看運算符優(yōu)先級。
運算符優(yōu)先級運算符優(yōu)先級表
而此題中出現(xiàn)了兩個操作符: 「!」, 「==」, 查表可知, 邏輯非優(yōu)先級是 16, 而等號優(yōu)先級是 10, 可見先執(zhí)行 ![] 操作。在此之前我們先看看 邏輯非
邏輯非 !mozilla 邏輯非: !
邏輯運算符通常用于Boolean型(邏輯)值。這種情況,它們返回一個布爾型值。
語法描述: 邏輯非(!) !expr
如果expr能轉(zhuǎn)換為true,返回false;
如果expr能轉(zhuǎn)換為false,則返回true。
轉(zhuǎn) booljs 中能夠轉(zhuǎn)換為false的字面量是可枚舉的,包含
null;
NaN;
0;
空字符串("");
undefined。
所以 ![] => false
于是乎我們將問題轉(zhuǎn)化為: [] == false== 運算符
這是個勁爆的操作符,正經(jīng)功能沒有,自帶隱式類型轉(zhuǎn)換經(jīng)常令人對 js 刮目相看, 實際上現(xiàn)在網(wǎng)上也沒有對這個操作符轉(zhuǎn)換規(guī)則描述比較好的,這個時候我們就需要去 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 則會將自身 ToNumber 轉(zhuǎn)換 !ToNumber(x) 參考 花絮下的 !ToNumber, 主要是講解 !的意思 ! 前綴在最新規(guī)范中表示某個過程會按照既定的規(guī)則和預(yù)期的執(zhí)行【必定會返回一個 number 類型的值,不會是其他類型,甚至 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)換為原始對象]
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. [如果一個對象可以被轉(zhuǎn)換為多種原語類型, 則參考 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 將每個屬性值 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[檢索對象的 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.
上述過程說的很明白: 如果 hint is String,并且他的 value 是 string 或者 number【ToPrimitive 中給 hint 打的標(biāo)簽】,接下來的處理邏輯,3,4 步描述的已經(jīng)很清楚了。
步驟 5,則是依次處理放入 methodNames 的操作[這也解答了我一直以來的一個疑問,網(wǎng)上也有說對象轉(zhuǎn) string 的時候,是調(diào)用 tostring 和 valueof, 但是總是含糊其辭,哪個先調(diào)用,哪個后調(diào)用,以及是不是兩個方法都會調(diào)用等問題總是模棱兩可,一句帶過 /手動狗頭]。
推論該了解的基本上都梳理出來了, 說實話,非常累,壓著沒有每個名詞都去發(fā)散。不過大致需要的環(huán)節(jié)都有了.
我們回過頭來看這個問題: 在對 == 操作符描述的步驟 8 9中,調(diào)用 ToPrimitive(y) 可見沒指定 PreferredType, 因此 hint 是 default,也就是 number【參考: 7.1.1ToPrimitive 的步驟2-f】
接著調(diào)用 OrdinaryToPrimitive(o, number) 則進入 7.1.1.1OrdinaryToPrimitive 的步驟 4 ,然后進入步驟 5 先調(diào)用 valueOf,步驟 5.2.2 描述中如果返回的不是 Object 則直接返回,否則才會調(diào)用 toString。
所以 [] == 0 => [].valueOf()[.toString()] == 0. 我們接著來看 數(shù)組的 valueOf 方法, 請注意區(qū)分一點,js 里內(nèi)置對象都繼承的到 valueOf 操作,但是部分對象做了覆寫, 比如 String.prototype.valueOf,所以去看看 Array.prototype.valueOf 有沒有覆寫。
結(jié)果是沒有,啪啪打臉啊,尼瑪,于是乎我們看 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【抓狂,但是要堅持】。
ToObjectECMAScript? 2019 : 7.1.13ToObject ( argument )
Object : Return argument?! 這步算是白走了。我們接著看 toString,同樣的我們要考慮覆寫的問題。
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).
可見調(diào)用了 join 方法【ps: 這里面還有個小故事,我曾經(jīng)去滴滴面試,二面和我聊到這個問題,我說數(shù)組的 toString 調(diào)用了 join ,面試官給我說,你不要看著調(diào)用結(jié)果就臆測內(nèi)部實現(xiàn),不是這樣思考問題的...... 我就搖了搖頭,結(jié)果止步二面,獵頭反饋的拒絕三連: 方向不匹配,不適合我們,滾吧。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99965.html
摘要:函數(shù)式編程前端掘金引言面向?qū)ο缶幊桃恢币詠矶际侵械闹鲗?dǎo)范式。函數(shù)式編程是一種強調(diào)減少對程序外部狀態(tài)產(chǎn)生改變的方式。 JavaScript 函數(shù)式編程 - 前端 - 掘金引言 面向?qū)ο缶幊桃恢币詠矶际荍avaScript中的主導(dǎo)范式。JavaScript作為一門多范式編程語言,然而,近幾年,函數(shù)式編程越來越多得受到開發(fā)者的青睞。函數(shù)式編程是一種強調(diào)減少對程序外部狀態(tài)產(chǎn)生改變的方式。因此,...
摘要:本文總結(jié)了前端老司機經(jīng)常問題的一些問題并結(jié)合個人總結(jié)給出了比較詳盡的答案。網(wǎng)易阿里騰訊校招社招必備知識點。此外還有網(wǎng)絡(luò)線程,定時器任務(wù)線程,文件系統(tǒng)處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機制叫做事件循環(huán)。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結(jié)了前端老司機經(jīng)常問題的一些問題并結(jié)合個...
摘要:本文總結(jié)了前端老司機經(jīng)常問題的一些問題并結(jié)合個人總結(jié)給出了比較詳盡的答案。網(wǎng)易阿里騰訊校招社招必備知識點。此外還有網(wǎng)絡(luò)線程,定時器任務(wù)線程,文件系統(tǒng)處理線程等等。線程核心是引擎。主線程和工作線程之間的通知機制叫做事件循環(huán)。 showImg(https://segmentfault.com/img/bVbu4aB?w=300&h=208); 本文總結(jié)了前端老司機經(jīng)常問題的一些問題并結(jié)合個...
摘要:與此相對,強類型語言的類型之間不一定有隱式轉(zhuǎn)換。三為什么是弱類型弱類型相對于強類型來說類型檢查更不嚴(yán)格,比如說允許變量類型的隱式轉(zhuǎn)換,允許強制類型轉(zhuǎn)換等等。在中,加性運算符有大量的特殊行為。 從++[[]][+[]]+[+[]]==10?深入淺出弱類型JS的隱式轉(zhuǎn)換 本文純屬原創(chuàng)? 如有雷同? 純屬抄襲? 不甚榮幸! 歡迎轉(zhuǎn)載! 原文收錄在【我的GitHub博客】,覺得本文寫的不算爛的...
摘要:插件開發(fā)前端掘金作者原文地址譯者插件是為應(yīng)用添加全局功能的一種強大而且簡單的方式。提供了與使用掌控異步前端掘金教你使用在行代碼內(nèi)優(yōu)雅的實現(xiàn)文件分片斷點續(xù)傳。 Vue.js 插件開發(fā) - 前端 - 掘金作者:Joshua Bemenderfer原文地址: creating-custom-plugins譯者:jeneser Vue.js插件是為應(yīng)用添加全局功能的一種強大而且簡單的方式。插....
閱讀 2304·2021-11-24 09:38
閱讀 2172·2021-11-22 14:44
閱讀 1162·2021-07-29 13:48
閱讀 2623·2019-08-29 13:20
閱讀 1123·2019-08-29 11:08
閱讀 2065·2019-08-26 10:58
閱讀 1270·2019-08-26 10:55
閱讀 3165·2019-08-26 10:39