摘要:將他們放在堆中是為了不影響棧的效率。所以簡單數據類型的值直接存放在棧中??梢詫Ρ壬厦婺菑垐D默認是調用方法的依,于是等于空字符串??兆址袊鴺藴蕰r間方法返回對象的原始值,可能是字符串數值或值等,看具體的對象。,需要兩個操作數同時轉為。
你是否在面試中遇到過各種奇葩和比較細節的問題?
[]==[] //false []==![] //true {}==!{} //false {}==![] //VM1896:1 Uncaught SyntaxError: Unexpected token == ![]=={} //false []==!{} //true undefined==null //true
看了這種題目,是不是想抽面試官幾耳光呢?哈哈,是不是看了之后一臉懵逼,兩臉茫然呢?心想這什么玩意
其實這些都是紙老虎,知道原理和轉換規則,理解明白這些很容易的,炒雞容易的,真的一點都不難,我們要打到一切紙老虎,不信?
我們就從[] == []和[] == ![]例子切入分析一下為什么輸出的結果是true而不是其它的呢?
[]==[]為什么是false?有點js基礎應該知道對象是引用類型,就會一眼看出來[] == []會輸出false,因為左邊的[]和右邊的[]看起來長的一樣,但是他們引用的地址并不同,這個是同一類型的比較,所以相對沒那么麻煩,暫時不理解[] == []為false的童鞋這里就不細說,想要弄清楚可以通過這篇文章來了解JavaScript的內存空間詳解.
前端基礎進階(一):內存空間詳細圖解
變量對象與堆內存
簡單類型都放在棧(stack)里 對象類型都放在堆(heap)里 var a = 20; var b = "abc"; var c = true; var d = { m: 20 }//地址假設為0x0012ff7c var e = { m: 20 }//重新開辟一段內存空間假設為0x0012ff8f console.log(e==d);//false
首先,我們來看一下代碼:
function Person(id,name,age){ this.id = id; this.name = name; this.age = age; } var num = 10; var bol = true; var str = "abc"; var obj = new Object(); var arr = ["a","b","c"]; var person = new Person(100,"笨蛋的座右銘",25);
然后我們來看一下內存分析圖:
`變量num,bol,str為基本數據類型,它們的值,直接存放在棧中,obj,person,arr為復合數據類型,他們的引用變量存儲在棧中,指向于存儲在堆中的實際對象。
由上圖可知,我們無法直接操縱堆中的數據,也就是說我們無法直接操縱對象,但我們可以通過棧中對對象的引用來操作對象,就像我們通過遙控機操作電視機一樣,區別在于這個電視機本身并沒有控制按鈕。
`
`記住一句話:能量是守衡的,無非是時間換空間,空間換時間的問題
堆比棧大,棧比堆的運算速度快,對象是一個復雜的結構,并且可以自由擴展,如:數組可以無限擴充,對象可以自由添加屬性。將他們放在堆中是為了不影響棧的效率。而是通過引用的方式查找到堆中的實際對象再進行操作。相對于簡單數據類型而言,簡單數據類型就比較穩定,并且它只占據很小的內存。不將簡單數據類型放在堆是因為通過引用到堆中查找實際對象是要花費時間的,而這個綜合成本遠大于直接從棧中取得實際值的成本。所以簡單數據類型的值直接存放在棧中。`
搬運文章:理解js內存分配
首先第一步:你要明白ECMAScript規范里面==的真正含義
GetValue 會獲取一個子表達式的值(消除掉左值引用),在表達式 [] == ![] 中,[] 的結果就是一個空數組的引用(上文已經介紹到數組是引用類型),而 ![] 就有意思了,它會按照 11.4.9 和 9.2 節的要求得到 false。
首先我們了解一下運算符的優先級:
剛看到這里是不是就咬牙切齒了,好戲還在后頭呢,哈哈
!取反運算符的優先級會高于==,所以我們先看看!在ECAMScript是怎么定義的?
所以![]最后會是一個Boolean類型的值(這點很關鍵,涉及到下面的匹配選擇).
兩者比較的行為在 11.9.3 節里,所以翻到 11.9.3:
在這段算法里,[]是Object,![]是Boolean,兩者的類型不同,y是Boolean類型,由此可知[] == ![]匹配的是條件 8,所以會遞歸地調用[] == ToNumber(Boolean)進行比較。
[]空數組轉化成Boolean,那么這個結果到底是true還是false呢,這個當然不是你說了算,也不是我說了算,ECMAScript定義的規范說了算:我們來看看規范:
[]是一個對象,所以對應轉換成Boolean對象的值為true;那么![]對應的Boolean值就是false
進而就成了比較[] == ToNumber(false)了
再來看看ECMAScipt規范中對于Number的轉換
由此可以得出此時的比較成了[]==0;
在此處因為 [] 是對象,0是數字Number,比較過程走分支 10(若Type(x)為Object且Type(y)為String或Number, 返回比較ToPrimitive(x) == y的結果。),可以對比上面那張圖.
ToPrimitive 默認是調用 toString 方法的(依 8.2.8),于是 ToPrimitice([]) 等于空字符串。
再來看看ECMAScript標準怎么定義ToPrimitice方法的:
是不是看了這個定義,還是一臉懵逼,ToPrimitive這尼瑪什么玩意啊?這不是等于沒說嗎?
再來看看火狐MDN上面文檔的介紹:
JS::ToPrimitive
查了一下資料,上面要說的可以概括成:
ToPrimitive(obj,preferredType) JS引擎內部轉換為原始值ToPrimitive(obj,preferredType)函數接受兩個參數,第一個obj為被轉換的對象,第二個 preferredType為希望轉換成的類型(默認為空,接受的值為Number或String) 在執行ToPrimitive(obj,preferredType)時如果第二個參數為空并且obj為Date的事例時,此時preferredType會 被設置為String,其他情況下preferredType都會被設置為Number如果preferredType為Number,ToPrimitive執 行過程如 下: 1. 如果obj為原始值,直接返回; 2. 否則調用 obj.valueOf(),如果執行結果是原始值,返回之; 3. 否則調用 obj.toString(),如果執行結果是原始值,返回之; 4. 否則拋異常。 如果preferredType為String,將上面的第2步和第3步調換,即: 1. 如果obj為原始值,直接返回; 2. 否則調用 obj.toString(),如果執行結果是原始值,返回之; 3. 否則調用 obj.valueOf(),如果執行結果是原始值,返回之; 4. 否則拋異常。
首先我們要明白obj.valueOf()和 obj.toString()還有原始值分別是什么意思,這是弄懂上面描述的前提之一:
toString用來返回對象的字符串表示。
var obj = {}; console.log(obj.toString());//[object Object] var arr2 = []; console.log(arr2.toString());//""空字符串 var date = new Date(); console.log(date.toString());//Sun Feb 28 2016 13:40:36 GMT+0800 (中國標準時間)
valueOf方法返回對象的原始值,可能是字符串、數值或bool值等,看具體的對象。
var obj = { name: "obj" }; console.log(obj.valueOf());//Object {name: "obj"} var arr1 = [1]; console.log(arr1.valueOf());//[1] var date = new Date(); console.log(date.valueOf());//1456638436303 如代碼所示,三個不同的對象實例調用valueOf返回不同的數據
**原始值指的是["Null","Undefined","String","Boolean","Number"]五種基本數據類型之一(我猜的,查了一下
確實是這樣的)**
弄清楚這些以后,舉個簡單的例子:
var a={}; ToPrimitive(a) 分析:a是對象類型但不是Date實例對象,所以preferredType默認是Number,先調用a.valueOf()不是原始值,繼續來調 用a.toString()得到string字符串,此時為原始值,返回之.所以最后ToPrimitive(a)得到就是"[object Object]".
如果覺得描述還不好明白,一大堆描述晦澀又難懂,我們用代碼說話:
const toPrimitive = (obj, preferredType="Number") => { let Utils = { typeOf: function(obj) { return Object.prototype.toString.call(obj).slice(8, -1); }, isPrimitive: function(obj) { let types = ["Null", "String", "Boolean", "Undefined", "Number"]; return types.indexOf(this.typeOf(obj)) !== -1; } }; if (Utils.isPrimitive(obj)) { return obj; } preferredType = (preferredType === "String" || Utils.typeOf(obj) === "Date") ? "String" : "Number"; if (preferredType === "Number") { if (Utils.isPrimitive(obj.valueOf())) { return obj.valueOf() }; if (Utils.isPrimitive(obj.toString())) { return obj.toString() }; } else { if (Utils.isPrimitive(obj.toString())) { return obj.toString() }; if (Utils.isPrimitive(obj.valueOf())) { return obj.valueOf() }; } } var a={}; ToPrimitive(a);//"[object Object]",與上面文字分析的一致
分析了這么多,剛才分析到哪里了,好像到了比較ToPrimitive([]) == 0現在我們知道ToPrimitive([])="",也就是空字符串;
那么最后就變成了""==0這種狀態,繼續看和比較這張圖
發現typeof("")為string,0為number,發現第5條滿足規則,最后就成了toNumber("")==0的比較了,根據toNumber的轉換規則:
所以toNumber("")=0,最后也就成了0 == 0的問題,于是[]==![]最后成了0 == 0的問題,答案顯而易見為true,一波三折
最后總結一下==運算規則的圖形化表示
前面說得很亂,根據我們得到的最終的圖3,我們總結一下==運算的規則: 1. undefined == null,結果是true。且它倆與所有其他值比較的結果都是false。 2. String == Boolean,需要兩個操作數同時轉為Number。 3. String/Boolean == Number,需要String/Boolean轉為Number。 4. Object == Primitive,需要Object轉為Primitive(具體通過valueOf和toString方法)。 瞧見沒有,一共只有4條規則!是不是很清晰、很簡單。主要參考文章和文獻
ECMAScript5.1規范中文版
通過一張簡單的圖,讓你徹底地、永久地搞懂JS的==運算
JavaScript中加號運算符的類型轉換優先級是什么?
喜歡我總結的文章對你有幫助有收獲的話麻煩點個star
我的github博客地址,總結的第一篇,不好之處和借鑒不得到之處還望見諒,您的支持就是我的動力!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/81645.html
摘要:還記得剛開始學習的時候,內存管理前端掘金作為一門高級語言,并不像低級語言那樣擁有對內存的完全掌控。第三方庫的行代碼內實現一個前端掘金前言本文會教你如何在行代碼內,不依賴任何第三方的庫,用純實現一個。 (譯) 如何使用 JavaScript 構建響應式引擎 —— Part 1:可觀察的對象 - 掘金原文地址:How to build a reactive engine in JavaSc...
摘要:還記得剛開始學習的時候,內存管理前端掘金作為一門高級語言,并不像低級語言那樣擁有對內存的完全掌控。第三方庫的行代碼內實現一個前端掘金前言本文會教你如何在行代碼內,不依賴任何第三方的庫,用純實現一個。 (譯) 如何使用 JavaScript 構建響應式引擎 —— Part 1:可觀察的對象 - 掘金原文地址:How to build a reactive engine in JavaSc...
摘要:這樣優化后我們最多進行次判斷即可,大大提高了代碼的性能。表達式的值具有離散性, 個人博客,點擊查看目錄,喜歡可以關注一下. 1.從[]==![]為true來剖析JavaScript各種蛋疼的類型轉換 2.吹毛求疵的追求優雅高性能JavaScript 李小龍說過:天下武功,無堅不摧,唯快不破.(真的說過嗎?)我想說的是:世間網站,完美體驗,唯快不破.(這個我承認我說過.) showImg...
摘要:昨天在微博上看到一篇文章,也寫數組去重,主要推崇的方法是將利用數組元素當作對象來去重。我在微博轉發了用對象去重不是個好辦法然后作者問什么才是推薦的方法。實例對象實例對象主要指通過構造函數類生成的對象。 本文同時發布于個人博客https://www.toobug.net/articl... JavaScript的數組去重是一個老生常談的話題了。隨便搜一搜就能找到非常多不同版本的解法。 昨...
閱讀 623·2023-04-26 02:08
閱讀 2654·2021-11-18 10:02
閱讀 3460·2021-11-11 16:55
閱讀 2341·2021-08-17 10:13
閱讀 2901·2019-08-30 15:53
閱讀 685·2019-08-30 15:44
閱讀 2545·2019-08-30 11:10
閱讀 1755·2019-08-29 16:57