個人JS日記-隱式轉換相關知識

隱式轉換比較是js中繞不過去的坎,就算有幾年經驗的工程師也頗有可能對這塊知識不夠熟悉。就算你知道使用===比較從而避免踩坑,可是團隊其它成員不必定知道有這樣或那樣的坑,有後端語言經驗的人經常會造成一個思惟誤區:「JS這門語言很簡單,看看語法再找幾個例子就能操做DOM,編寫特效了」。隨着react、vue、html5等技術在項目中大規模使用,愈來愈多的項目中使用了大量的JS,甚至整個項目都用JS來寫(例如基於webapi的SPA管理後臺、微信小程序、微信H5應用,Hybrid app),若是不深刻的去學習JS,不改變思惟誤區,那麼你的程序頗有可能在這些地方產生BUG,這種隱藏的bug若是你對js不熟悉的話會很是難以查找。 下面開始今天的討論,請先在紙上寫出你的答案。html

console.log(newString('abc')==true)
console.log({}==true)
console.log([]==![])
複製代碼

若是您的答案是:true true false,那麼恭喜你,你所有都答錯了,你的思路可能以下:vue

1.new String('123')創造出來的是一個字符串,非空字符串轉成布爾值是true,true==true,結果天然爲true
2.{}是一個字面量對象,對象轉換成布爾值是true,true==true,結果天然爲true
3.右側有!運算符,優先級最高,先轉右邊。數組是一個對象,對象轉換成布爾值結果是true,![]的結果爲false,要比較的表達式變成了[]=false,而後數組轉成布爾值爲true,true==false,結果是falsehtml5

爲何你一個題都作不對呢?若是你頗有多是你沒有掌握到js中對象的概念,null的特殊規則,以及==運算符的轉換規則,下面是涉及到的一些知識。react

1、變量採用字面量形式、包裝器方式,new 方式的區別

var a='xxx'                  //申明的是一個string類型,它是一個基本類型
var a=String('abc')          // String()是一個包裝類,用於將參數轉換成string類型
var a=new String('abc')     //採用new方式時建立了一個object類型
複製代碼

使用typeof驗證上面的結論es6

var a='abc'
console.log(typeof a) //string
console.log(typeof String('abc')) //string
console.log(typeof(new String('abc'))) //object
複製代碼

哪些類型是object類型呢?用new 方法建立出來的確定是object類型,除此以外,字面量對象(即{ }) 數組、日期、正則表達式、Math、函數表達式、函數申明,都是object類型。
有同窗說不對呀,var a=function(){}和function a(){} 我用typeof輸出時明明是function啊,你怎麼說是object類型呢?爲何說函數申明或函數表達式是object類型呢,由於申明的時侯至關於調用了new Function('參數1','參數2',''函數體),使用new建立的確定就是object類型了,至於typeof對函數返回的是function,這是一個歷史遺留問題。web

2、JavaScript數據類型

使用相等於符時基本類型和object類型的規則不一樣,因此咱們有必要再次回顧一下js中的數據類型。 JavaScript有六種基本數據類型:string、boolean、number、null、undefined、symbol(es6新添加),還有一種複雜數據類型:object
按照數據在內存中的存儲方式,可將數據類型劃分爲值類型和引用類型。值類型的數據是存在棧中的,而引用類型則是在棧中會有一個指針,好比對象名,函數名等,它指向堆中的數據。基本數據類型都是值類型,object類型是引用類型,下面是存儲示意圖。 面試

image
知道了這個知識點後,之後遇到深拷貝問題就很好理解了。

3、各類類型隱式轉換到布爾類型對照表

爲何要忽然列這個呢,由於使用頻率比較高,面試時常常會出現相關的題目,因此須要牢記。好比有一個變量a,咱們須要判斷變量a不爲undefined時才執行代碼,若是你不知道隱式轉換,你頗有可能會寫成if(a===undefined),這是沒問題的,關鍵是可貴敲,並且多了幾個字節出來。我這裏直接摘抄了js高程設計上的表。 正則表達式

convert.png

4、!的轉換規則

使用該符號時,首先將操做符後面的數據轉成布爾類型,而後再取反,沒什麼特殊的。這個運算符有一個很經典的應用,強制讓一個變量的值只能爲true或false小程序

var a;
var b=!!a;
複製代碼

這段代碼的做用就是變量b的值只能爲true或false,當a爲undefined時b的值爲false,不然爲true,固然也能夠採用var b=a || false來達到相同的效果,我我的更喜歡後面這種。 這背後的原理正是上面的第三條,因此說緊緊理解隱式轉換是很是有必要的後端

5、==的轉換規則

比較操做符會爲兩個不一樣類型的操做數轉換類型,而後進行嚴格比較。當兩個操做數都是對象時,JavaScript會比較其內部引用,當且僅當他們的引用指向內存中的相同對象(區域)時才相等,即他們在棧內存中的引用地址相同。  爲了以防被博客上看到的一些總結誤導,我特意查詢了MDN上對類型轉換規則的說明:

  1. 當比較數字和字符串時,字符串會轉換成數字值。 JavaScript 嘗試將數字字面量轉換爲數字類型的值。
  2. 若是其中一個操做數爲布爾類型,那麼先將布爾類型轉換爲數字類型。
  3. 若是一個對象與數字或字符串向比較,JavaScript會嘗試返回對象的默認值。操做符會嘗試經過方法valueOf和toString將對象轉換爲其原始值(一個字符串或數字類型的值)。若是嘗試轉換失敗,會產生一個運行時錯誤。
  4. 注意:當且僅當與原始值比較時,對象會被轉換爲原始值。當兩個操做數均爲對象時,它們做爲對象進行比較,僅當它們引用相同對象時返回true。

官方的文檔讀起來老是有一些拗口,翻譯成大白話再加上JS高程設計書上說的特殊狀況,總結起來就如下幾點:

1.轉換時若是兩邊都是引用類型,則直接比較內存中的地址(也就是指針指向的地址)

console.log([]==[]) //false,指針指向的地址不一樣
複製代碼

2.若是兩邊類型不一致,則兩邊都轉成number類型,引用類型先調用valueOf()方法,若是能轉成數字就OK,不能轉成數字的話,就調用toString()轉成字符串。

var a='123'
  console.log(a==false)  //false,'123'轉成數字是123,右側轉成數字是0,最終比較123==0
 console.log(a==123)  //true,右邊是數字,直接轉換左右便可
複製代碼

object類型的比較

var a=new String(123)
 console.log(a==123) //true,a.valueOf()結果就是數字123,最終比較的是123==123
複製代碼

再來一個例子

var a={} 
console.log(a==1)
//上面a==1在js解釋引擎中的執行過程以下:
//a.valueOf()獲取到的不是基本類型,調用a.toString()獲得'[object Object]'
'[object Object]'==1;
//兩邊類型不致,左側轉成數字
NaN==1;//false,NaN跟任何類型比較都爲false
複製代碼

3.null、NaN、undefined和string、number、boolean、object類型比較時,都不作隱式轉換,比較的結果直接爲false。可是須要注意如下幾個規則:

console.log(NaN==NaN) //false
   console.log(undefined==null) //true
   console.log(null==null) //true
   console.log(null==undefined) //true
複製代碼

搞清楚了上述規則,開始那幾個題就特別簡單了,並且萬變不離其宗。咱們一步一步的來分析一下。

console.log(new String('abc')==true)
  //step1:右側轉成數字
  new String('abc')==1
  //step2 new String('abc').valueOf()不是數字也不是字符串,再調用toString()
  '[object Object]' ==1
 //step3:字符串轉數字
  NaN==1 //false,NaN和任何類型比較都爲false

  console.log({}==true)
  //step1:右側轉成數字
   {}==1
  //step2 {}.valueOf()不是數字也不是字符串,再調用toString()
  '[object Object]' ==1  
  //step3:字符串轉數字
   NaN==1 //false,NaN和任何類型比較都爲false

  console.log([]==![])
   //step1:!優先級比==高,先轉右邊,[]是對象類型,轉成布爾值爲true,!true就是false
   []==false
   //step2:右側轉成數字爲0
   []==0
   //step3:左側是一個對象,valueOf()轉出來不是字符也不是字符串,調用toString(),獲得空字符串
   ''==0
  //step4:字符串轉成數字
  0==0 //true
複製代碼

是否是還記不住規則,借用了一下高中政治書上的口吻,就很容易記住了:一箇中心(左右兩邊轉換成number爲中心),兩個基本點(轉換條件:1.類型不一樣時才轉換 2.兩邊都是引用類型時直接比較地址),一國兩制(null、NaN、undefined使用一套製做,其它的使用另外一套制度)

6、大於或小於符

先來看一個可能會讓你條件反射般陷入思惟誤區的坑

console.log('23'<'3') 
複製代碼

若是你得出的結果是false,那麼你腦子裏極有可能對這個表達式進行了一個隱式轉換,轉成了數字,正確的結果是false,爲何呢? 字符串類型比較大小時,不進行類型轉換,而是逐位比較ascii碼,第1位不一樣則返回結果,不然繼續比較第2位,直到某一位不一樣爲止。上面的例子中js引擎在背後作了以下規則(請忽視我把變量分紅了兩行寫,我這裏不考慮性能:))

var a='23'.charCodeAt(0) //50
var b='3'.charCodeAt(0) //51
50<51 //false
複製代碼

你可能會說哪一個腦殘會寫這種代碼,還真不必定,當<左右兩邊都是變量時就極有可能產生這種錯誤,另外一個涉及到比較時的坑就是數組排序。

var a=[1,10,3,100].sort()
複製代碼

你指望的結果是[1,3,10,100],但是結果再次讓你失望了,緣由是sort()方法默認的比較規則會先把每一個元素轉成字符串,而後比較字符串的ascii碼來肯定前後順序。

7、+號規則

+號運算符便可以對兩個數相加,也能夠鏈接字符串,那若是是[1,2,3]+4這種狀況下又會發生什麼呢?這就須要咱們瞭解相應的規則,爲了方便描述,咱們把+號左側的值叫作A,右側的叫作B:
第一步:若是A和B都是number類型,直接相加;
第二步:接下來看A或B中是否有一個是否爲string類型,若是有,則將另外一個也轉成字符串,而後鏈接
第三步:既不是number,也不是string,則按以下規則轉換:
1.能轉換成數字,返回之
2.不然調用valueOf(),若是執行結果是基本類型,返回之;
3.不然調用toString(),若是執行結果是基礎類型,返回之;
4.沒法獲得原始值,拋異常。

valueOf和toString參考.png
相關文章
相關標籤/搜索