1. 程式人生 > >js學習日記-隱式轉換相關的坑及知識

js學習日記-隱式轉換相關的坑及知識

ber 可能 port 思路 布爾值 對象 成了 tps text

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

請先在紙上寫出下面代碼會輸出什麽開始我們今天的知識總結。

  console.log(new String(‘abc‘)==true)
  console.log({}==true)
  console.log([]==![])

如果您的答案是:true true false,那麽恭喜你,你全部都答錯了,你的思路可能如下:

1.new String(‘123‘)創造出來的是一個字符串,非空字符串轉成布爾值是true,true==true,結果自然為true

2.{}是一個字面量對象,對象轉換成布爾值是true,true==true,結果自然為true

3.右側有!運算符,優先級最高,先轉右邊。數組是一個對象,對象轉換成布爾值結果是true,![]的結果為false,要比較的表達式變成了[]=false,然後數組轉成布爾值為true,true==false,結果是false

為什麽你一個題都做不對呢?如果你很有可能是你沒有掌握到js中對象的概念,null的特殊規則,以及==運算符的轉換規則,下面就開始展開相應的知識。

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

var a=‘xxx‘ 申明的是一個string類型,它是一個基本類型

var a=String(‘abc‘), String()是一個包裝類,用於將參數轉換成string類型

var a=new String(‘abc‘) 當在函數前采用new時,相當於創建了一個object類型

  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類型呢?因為采用函數表達式或函數聲明的時似,實際上JS引擎在後面做了一個工作,相當於調用了new Function(‘參數1‘,‘參數2‘,‘‘函數體),使用new創建的,肯定就是object類型了,至於typeof對函數返回的是function,這是一個特例。

二、JavaScript數據類型

JavaScript有六種基本數據類型:string、boolean、number、null、undefined、symbol(es6新添加),還有一種復雜數據類型:object

基本類型和object類型在內存中存儲的方式是不一致的,基本類型數據存放在棧中(值類型),object類型(引用類型)在棧中會有一個指針(通常是函數名)指向堆中的數據。知道了這個知識點才知道==操作符的相關規則,才知道淺拷貝和深拷貝的坑。

技術分享圖片

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

數據類型 轉換為true的值 轉換為false的值
Boolean true false
String 任何非空字符串 "" (空字符串)
Number 任何非零數字值(包括無窮大) 0和NaN
Object 任何對象 null
Undefined 不適用 undefine

四、邏輯!的轉換規則

使用該符號時,首先將操作符後面的數據轉成布爾類型,然後再取反,沒什麽特殊的。這個運算符有一個很經典的應用。

var a;
var b=!!a;

這段代碼的作用就是變量b的值只能為true或false,當a為undefined時b的值為false,否則為true

當然也可以采用var b=a || false來達到相同的效果,我個人更喜歡後面這種。

這背後的原理正是應用了上面第三大點的知識,所以說牢牢理解隱式轉換是非常有必要的

五、相等比較==的規則

比較操作符會為兩個不同類型的操作數轉換類型,然後進行嚴格比較。當兩個操作數都是對象時,JavaScript會比較其內部引用,當且僅當他們的引用指向內存中的相同對象(區域)時才相等,即他們在棧內存中的引用地址相同。

為了以防被博客上看到的一些總結誤導,我特地查詢了MDN上對類型轉換規則的說明:

  • 當比較數字和字符串時,字符串會轉換成數字值。 JavaScript 嘗試將數字字面量轉換為數字類型的值。
  • 如果其中一個操作數為布爾類型,那麽先將布爾類型轉換為數字類型。
  • 如果一個對象與數字或字符串向比較,JavaScript會嘗試返回對象的默認值。操作符會嘗試通過方法valueOf和toString將對象轉換為其原始值(一個字符串或數字類型的值)。如果嘗試轉換失敗,會產生一個運行時錯誤。
  • 註意:當且僅當與原始值比較時,對象會被轉換為原始值。當兩個操作數均為對象時,它們作為對象進行比較,僅當它們引用相同對象時返回true。

從上面這段權威的規則可以得出以下結論:

1.如果兩邊類型不一致,但兩邊都屬於string、number、boolean中的一種,則首先嘗試將兩邊都轉化成數字類型來比較(上面第1、2條總結起來)

  var a=‘123‘
  console.log(a==false)  //false,‘123‘轉成數字是123,右側轉成數字是0,最終比較123==0

2.如果一邊是object類型,另一邊是string或number(boolean類型也會轉換成數字),則先看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

上面兩條得出一個規律,相等比較就是把左邊和右邊轉成number類型來比較

3.如果兩邊都是引用類型,不進行轉換,直接比較內存中引用的是否是同一個地址。

  console.log([]==[]) //false,指針指向的地址不同

4.null和undefined跟其它任何類型比較時,不做轉換,也就是跟其它類型比較時結果總是為false,但undefined==null的結果總是為true

5.NaN跟任何類型比較都為false,NaN==NaN的結果也為false

搞清楚了上述規則,開始那幾個題就特別簡單了,而且萬變不離其宗。

  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

六、大於或小於符的坑

先來看一個又會讓你大吃一驚的例子

 console.log(‘23‘<‘3‘) //true

如果你回答是false,那麽你又該看一看了,為什麽會是true呢?因為字符串類型比較大小時,不是進行隱式轉換,而是逐位比較ascii碼,第1位不同則返回結果,否則繼續比較第2位,直到某一位不同為止,上面的例子js引擎是這麽來處理的

var a=‘23‘.charCodeAt(0) //50

var b=‘3‘.charCodeAt(0) //51

50<51 //false

比較大小時如果左右兩邊是變量,建議最好先轉成數字來比較。

七、0.1+0.2==0,3為false的坑

答案不是true,是不是想打人,js中兩個浮點數相加,如果結果不是整數,那麽小數點後面默認還有很多位,這裏有詳細說明0.1 + 0.2 === 0.30000000000000004的背後

所以我們在類似的場景時,最好采用(0.1+0.2).toFixed(1)把小數點位數弄統一再進行比較。

js學習日記-隱式轉換相關的坑及知識