前言
相信剛開始瞭解js的時候,都會遇到 2 == '2',但 1+2 == 1+'2'爲false的狀況。這時候應該會是一臉懵逼的狀態,不得不感慨js弱類型的靈活讓人髮指,隱式類型轉換就是這麼猝不及防。結合實際中的狀況來看,有意或無心中涉及到隱式類型轉換的狀況仍是不少的。既然要用到,就須要掌握其原理,知其然重要知其因此然更重要。git
<!--more -->es6
js的變量類型
JavaScript 是弱類型語言,意味着JavaScript 變量沒有預先肯定的類型。 而且變量的類型是其值的類型。也就是說變量當前的類型由其值所決定,誇張點說上一秒種的string,下一秒可能就是個array了。此外當進行某些操做時,變量能夠進行類型轉換,咱們主動進行的就是顯式類型轉換,另外一種就是隱式類型轉換了。例如:github
var a = '1'; typeof a;//string a =parseInt(a); //顯示轉換爲number typeof a //number a == '1' //true
弱類型的特性在給咱們帶來便利的同時,也會給咱們帶來困擾。趨利避害,充分利用該特性的前提就是掌握類型轉換的原理,下面一塊兒看一下。算法
js數據類型
老生常談的兩大類數據類型:學習
- 原始類型 Undefined、 Null、 String、 Number、 Boolean
- 引用類型 object 此外還有一個es6新增的Symbol,先不討論它。對於這五類原始類型,忽然提問可能想不全,不必去死記硬背,能夠想一下爲否的常見變量及其對應值便可。
0 | Number |
---|---|
'' | String |
false | Boolean |
null | Null |
undefined | Undefined |
對於不一樣的數據格式轉換規則是不一樣的,咱們須要分別對待。this
轉換規則
既然是規範定義的規則,那就不要問爲何了,先大體看一下,爭取記住。是在不行常常翻翻看看大佬的博客es5規範。轉換有下面這麼幾類,咱們分別看一下具體規範。(這部分轉換規則,徹底能夠跳過去,看到下面的實例再回頭看應該更容易接受一些)url
- 轉換爲原始值
- 轉換爲數字
- 轉換爲字符串
ToPrimitive(轉換爲原始值)
ToPrimitive 運算符接受一個值,和一個可選的 指望類型 做參數。ToPrimitive 運算符把其值參數轉換爲非對象類型。若是對象有能力被轉換爲不止一種原語類型,可使用可選的 指望類型 來暗示那個類型。根據下表完成轉換 <img src='http://xxdy.tech/img/jstype/1.png'>es5
這段定義看起來有點枯燥。轉換爲原始值,其實就是針對引用數據的,其目的是轉換爲非對象類型。 若是已是原始類型,固然就不作處理了 對於object,返回對應的原始類型,該原始類型是由指望類型決定的,指望類型其實就是咱們傳遞的type。直接看下面比較清楚。 ToPrimitive方法大概長這麼個樣子具體以下。spa
/** * @obj 須要轉換的對象 * @type 指望轉換爲的原始數據類型,可選 */ ToPrimitive(obj,type)
type能夠爲number或者string,二者的執行順序有一些差異 string:.net
- 調用obj的toString方法,若是爲原始值,則返回,不然下一步
- 調用obj的valueOf方法,後續同上
- 拋出TypeError 異常
number:
- 調用obj的valueOf方法,若是爲原始值,則返回,不然下一步
- 調用obj的toString方法,後續同上
- 拋出TypeError 異常
其實就是調用方法前後,畢竟指望數據類型不一樣,若是是string固然優先調用toString。反之亦然。 固然type參數能夠爲空,這時候type的默認值會按照下面的規則設置
- 該對象爲Date,則type被設置爲String
- 不然,type被設置爲Number
對於Date數據類型,咱們更多指望得到的是其轉爲時間後的字符串,而非毫秒值,若是爲number,則會取到對應的毫秒值,顯然字符串使用更多。 其餘類型對象按照取值的類型操做便可。
歸納而言,ToPrimitive轉成何種原始類型,取決於type,type參數可選,若指定,則按照指定類型轉換,若不指定,默認根據實用狀況分兩種狀況,Date爲string,其他對象爲number。那麼何時會指定type類型呢,那就要看下面兩種轉換方式了。
toNumber
某些特定狀況下須要用到ToNumber方法來轉成number 運算符根據下表將其參數轉換爲數值類型的值 <img src='http://xxdy.tech/img/jstype/2.png'>
對於string類型,狀況比較多,只要掌握常見的就好了。和直接調用Number(str)的結果一致,這裏就很少提了,主要是太多提不完。 須要注意的是,這裏調用ToPrimitive的時候,type就指定爲number了。下面的toString則爲string。
toString
ToString 運算符根據下表將其參數轉換爲字符串類型的值: 其實瞭解也很簡單,畢竟是個規範,借用大佬一張圖: <img src='http://xxdy.tech/img/jstype/3.png'>
雖然是須要死記的東西,仍是有些規律可循的。 對於原始值:
- Undefined,null,boolean 直接加上引號,例如'null'
- number 則有比較長的規範,畢竟範圍比較大 常見的就是 '1' NaN則爲'NaN' 基本等同於上面一條 對於負數,則返回-+字符串 例如 '-2' 其餘的先不考慮了。
- 對象則是先轉爲原始值,再按照上面的步驟進行處理。
valueOf
當調用 valueOf 方法,採用以下步驟:
- 調用ToObject方法獲得一個對象O
- 原始數據類型轉換爲對應的內置對象, 引用類型則不變
- 調用該對象(O)內置valueOf方法.
不一樣內置對象的valueOf實現:
- String => 返回字符串值
- Number => 返回數字值
- Date => 返回一個數字,即時間值,字符串中內容是依賴於具體實現的
- Boolean => 返回Boolean的this值
- Object => 返回this
對照代碼更清晰一點
var str = new String('123') //123 console.log(str.valueOf()) var num = new Number(123) //123 console.log(num.valueOf()) var date = new Date() //1526990889729 console.log(date.valueOf()) var bool = new Boolean('123') //true console.log(bool.valueOf()) var obj = new Object({valueOf:()=>{ return 1 }}) //依賴於內部實現 console.log(obj.valueOf())
運算隱式轉換
前面提了那麼多抽象概念,就是爲了這裏來理解具體轉換的。 對於+運算來講,規則以下:
- +號左右分別進行取值,進行ToPrimitive()操做
- 分別獲取左右轉換以後的值,若是存在String,則對其進行ToString處理後進行拼接操做。
- 其餘的都進行ToNumber處理
- 在轉換時ToPrimitive,除去Date爲string外都按照ToPrimitive type爲Number進行處理 說的本身都迷糊了快,一塊兒結合代碼來看一下
1+'2'+false
- 左邊取原始值,依舊是Number
- 中間爲String,則都進行toString操做
- 左邊轉換按照toString的規則,返回'1'
- 獲得結果temp值'12'
- 右邊布爾值和temp一樣進行1步驟
- temp爲string,則布爾值也轉爲string'false'
- 拼接二者 獲得最後結果 '12false'
咱們看一個複雜的
var obj1 = { valueOf:function(){ return 1 } } var obj2 = { toString:function(){ return 'a' } } //2 console.log(1+obj1) //1a console.log('1'+ obj2) //1a console.log(obj1+obj2)
無論多複雜,按照上面的順序來吧。
- 1+obj1
- 左邊就不說了,number
- 右邊obj轉爲基礎類型,按照type爲number進行
- 先調用valueOf() 獲得結果爲1
- 兩遍都是number,則進行相加獲得2
- 1+obj2
- 左邊爲number
- 右邊一樣按照按照type爲number進行轉化
- 調用obj2.valueOf()獲得的不是原始值
- 調用toString() return 'a'
- 依據第二條規則,存在string,則都轉換爲string進行拼接
- 獲得結果1a
- obj1+obj2
- 兩邊都是引用,進行轉換 ToPrimitive 默認type爲number
- obj1.valueOf()爲1 直接返回
- obj2.valueOf()獲得的不是原始值
- 調用toString() return 'a'
- 依據第二條規則,存在string,則都轉換爲string進行拼接
- 獲得結果1a
到這裏相信你們對+這種運算的類型轉換了解的差很少了。下面就看一下另外一種隱式類型轉換
== 抽象相等比較
這種比較分爲兩大類,
- 類型相同
- 類型不一樣 相同的就不說了,隱式轉換髮生在不一樣類型之間。規律比較複雜,規範比較長,這裏也不列舉了,你們能夠查看抽象相等算法。簡單總結一句,相等比較就不想+運算那樣string優先了,是以number優先級爲最高。歸納而言就是,都儘可能轉成number來進行處理,這樣也能夠理解,畢竟比較仍是指望比較數值。那麼規則大概以下: 對於x == y
- 若是x,y均爲number,直接比較
沒什麼可解釋的了 1 == 2 //false
- 若是存在對象,ToPrimitive() type爲number進行轉換,再進行後面比較
var obj1 = { valueOf:function(){ return '1' } } 1 == obj2 //true //obj1轉爲原始值,調用obj1.valueOf() //返回原始值'1' //'1'toNumber獲得 1 而後比較 1 == 1 [] == ![] //true //[]做爲對象ToPrimitive獲得 '' //![]做爲boolean轉換獲得0 //'' == 0 //轉換爲 0==0 //true
- 存在boolean,按照ToNumber將boolean轉換爲1或者0,再進行後面比較
//boolean 先轉成number,按照上面的規則獲得1 //3 == 1 false //0 == 0 true 3 == true // false '0' == false //true
- 若是x爲string,y爲number,x轉成number進行比較
//'0' toNumber()獲得 0 //0 == 0 true '0' == 0 //true
結束語
參考文章
ECMAScript5.1中文版 + ECMAScript3 + ECMAScript(合集) 你所忽略的js隱式轉換 這篇文章的本意是爲本身解惑,寫到後面真的感受比較乏味,畢竟規範性的東西多一點,不過深刻了解一下總好過死記硬背。原文請移步個人博客。對於有些觀點說這些屬於js糟粕,徹底不該該深刻,怎麼說呢,結合本身狀況判斷吧。本人水平有限,拋磚引玉共同窗習。