摘要:等同于等同于其他類型和布爾類型之間的比較如果是布爾類型,則返回的結果。
前言
JavaScript作為一門弱類型語言,我們在每天的編寫代碼過程中,無時無刻不在應用著值類型轉換,但是很多時候我們只是在單純的寫,并不曾停下腳步去探尋過值類型轉換的內部轉換規則,最近通過閱讀你不知道的JavaScript中篇,對js的值類型轉換進行了更加深入的學習,在此分享給大家參考學習。概念
將值從一種類型轉換為另一種類型通常稱為類型轉換,主要發生在靜態語言的編譯階段;強制類型轉換則發生在動態語言的運行階段;JavaScript作為一門典型的動態弱類型語言自然而然采用的是強制類型轉換(即隱式強制類型轉換和顯式強制類型轉換);在js的強制類型轉換總是返回標量基本類型值,如字符串、布爾、數字,不會返回對象和函數
var a = 42; var b = a + "";//隱式強制類型轉換 var c = String(a);//顯式強制類型轉化前情提要
在閱讀后面的內容之前,我們首先要明白下面幾個概念,以方便對后續內容的理解
封裝對象 :eg:var a = new String("abc"),a被叫做封裝了基本類型的封裝對象,還原一個封裝對象的值,可以調用valueOf方法;
基本類型的幾乎所有方法并非來自本身,而是來自于封裝對象的原型對象,例如下面例子
const a = 1.2; console.log(a.toFixed(0));//1
基本類型數字并不存在toFixed方法,只是在訪問該方法時候,js自動封裝基本類型為對應的封裝對象,再去訪問該封裝對象的原型上對應的方法,等同于下面例子
const a = 1.2; console.log(new Number(a).__proto__.toFixed());//0
ToPrimitive抽象操作:該操作主要是將對象類型轉換為基本類型,首先檢查某個對象是否有valueOf屬性,如果有則返回該對象的valueOf的值,否則調用該對象的toString屬性并返回值(如果valueOf返回的不是基本類型則調用toString方法,例如數組的valueOf返回的還是數組,所有ToPrimitive會默認調用toString方法);
抽象值操作 ToString負責處理非字符串到字符串的強制類型轉換,規則如下:1.null轉換為"null",undefined轉換為"undefined",其他基本類型都調用基本類型的包裝對象屬性toString()并返回值。
const a = 123; const _a = new Number(123); console.log(String(a), _a.toString());//"123" "123" const b = true; const _b = new Boolean(true); console.log(String(b), _b.toString());//"true" "true"
2.數字的字符串化遵循通用規則,但是極小極大數字使用指數形式
const a = 1.07*1000*1000*1000*1000*1000*1000*1000; console.log(String(a));//"1.07e+21"
3.對于普通對象來說,除非自行定義,否則toString()返回Object.prototype.toString()的值,其他對象有自己的toString()方法則調用自己的該方法
const b = {}; console.log(String(b));//[object object]
4.數組的默認toString()方法進行了重新定義,將所有單元字符串化以后再用‘,’連接起來
const a = [1, 2, 3]; console.log(String(a));//"1,2,3"
5.JSON字符串化
5-1.JSON字符串化和toString的效果基本相同,只不過序列化的結果總是字符串
5-2.JSON對于不安全的值(undefined,function(){},symbol)直接忽略,數組中則以null填充
5-3.對象循環引用直接報錯,正則表達式序列化為{}
ToNumber負責處理非數字到數字的強制類型轉換,規則如下:1.true轉換為1,false轉換為0,undefined轉換為NaN,null轉換為0
console.log(Number(null));//0 console.log(Number(undefined));//NaN console.log(Number(true));//1 console.log(Number(false));//0
2.對字符串的處理遵循數字常量的相關規定/語法,處理失敗時返回NaN
console.log(Number("123"));//123 console.log(Number("0b111"));//7 console.log(Number("0o123"));//83 console.log(Number("0x123"));//291 console.log(Number("123a"));//NaN console.log(Number("a123"));//NaN
3.對象(包括數組)會首先按照ToPrimitive抽象操作被轉換為相應的基本類型值,再按照前兩條規則處理;如果某個對象即不存在valueOf方法也不存在toString方法,則會產生TypeError錯誤(例如Object.create(null)不存在以上兩種方法)
const arr = [1, 2, 3]; console.log(Number(arr));//NaN console.log(Number(arr.toString()));//NaN const num = new Number(123); console.log(Number(num));//123 console.log(Number(num.valueOf()));//123 const bool = new Boolean(true); console.log(bool.valueOf());//true console.log(Number(bool));//1 console.log(Number(bool.valueOf()));//1 const obj1 = { toString:()=>"21" } const obj2 = { valueOf:()=>"42", toString:()=>"21" } const obj3 = { a:1 } console.log(Number(obj1));//21 console.log(Number(obj2));//42 console.log(obj3.toString());//[object Object] console.log(Number(obj3));//NaN const obj = Object.create(null); console.log(Number(obj));//TypeError
上述obj1,obj2分別調用toString和valueOf方法,obj3調用原型上的toString()方法
ToBoolean負責處理非布爾值到布爾值的強制類型轉換,規則如下:可以被轉換為false的值(undefined,null,false, +0、-0和NaN,"")
除條件1的其他都被轉換為true(切記:封裝對象均被轉為true)
顯式強制類型轉換 字符串和數字之間的顯式轉換通過window下面的內建函數String()和Number()來實現(遵循上述抽象值操作)
toString()的顯式轉換過程為先隱式的將基本類型轉為封裝對象,再對該對象調用toString方法
一元運算符+和-來轉換,例如+a顯式的將c轉換為數字
位運算NOT的顯式轉換
第一步:位運算NOT將非數字和數字轉換為32位數字
第二步:將該數字求負值并減1
indexOf優雅寫法(返回-1的這種現象稱為抽象滲漏,即代碼暴露了底層的實現細節)
截除數字值得小數部分比Math.floor()更加靠譜(Math.floor對負數的截取和~不同)
const a = true; console.log(~a === -Number(a)-1)//true //indexOf優雅寫法 const b = "abc"; if(~b.indexOf("d")){ console.log("存在d") }else{ console.log("不存在d") } //數字的小數部分截除 const d = 3.14; const e = -3.14; console.log(Math.floor(d), ~~d);//3 3 console.log(Math.floor(e), ~~e);//-4 -3顯式解析數字字符串
解析字符串中的數字和強制將字符串轉換為數字返回的結果都是數字;但是解析允許字符串中含有非數字,解析按從左到右的順序,如果遇到非數字就停止解析;而轉換不允許出現非數字字符,否則會失敗并返回NaN。
parseInt()和parseFloat分別用來解析整數和浮點數,傳入的值必須是字符串,如果是非字符串會被隱式轉換為字符串再解析
注意點:es5之前parseInt()遇到0開頭的字符串數字(比如時間hour="09")會被按照八進制處理,需要在第二個參數傳入10解決,es5之后0開頭的能能字符串化為八進制的按八進制解析不能的按10進制解析;
//現將a轉為字符串"1,2,3" const a = [1, 2, 3]; console.log(parseInt(a));//1 console.log(parseFloat(a));//1 //現將true轉為字符串"true" console.log(parseInt(true));//NaN console.log(parseFloat(true));//NaN //現將3.14轉為字符串"3.14" console.log(parseInt(3.14));//3 console.log(parseFloat(3.14));//3.14 console.log(String(new Date()));//"Wed Jun 12 2019 21:23:59 GMT+0800" console.log(parseInt(new Date()));//NaN console.log(parseFloat(new Date()));//NaN //關于es6之前八進制寫法的解析 console.log(parseInt(09));//9 console.log(parseFloat(09));//9 console.log(parseInt(010));//8 console.log(parseFloat(010));//8
parseInt()的一些奇怪現象
parseInt(1/0, 19);//18 //其實相當于parseInt(Infinity, 19);其中Infinity的I在十九進制數中為18 parseInt(0.000008);//0 //字符串化為0.00008后進行解析 parseInt(0.0000008);//8 //字符化為8e-7后進行解析(詳見抽象ToNumber) parseInt(0x10);//16 String(0x10);//16 parseInt(0b10);//2 String(0b10);//2 parseInt(0o10);//8 String(0o10);//8 parseInt(012);//10 String(012);//10 //其實現在es6規定了二進制、八進制和十六進制的表示法 //以上三個字符串均先被String()化為字符串再進行解析顯式轉換為布爾值
通過全局方法Boolean()強制轉換,遵循抽象值操作中的ToBolean
!!進行強制轉換,遵循抽象值操作中的ToBolean
隱式強制類型轉換 隱式強制類型轉換為字符串一元運算符加號(+)首先把非基本類型通過ToPrimitive抽象操作轉換為基本類型,如果加號中的兩項有一項是字符串,另一項則進行ToString操作,進行字符串拼接,如果是布爾值加數字,則對布爾進行ToNumber操作再求和
const a = 1; console.log(a + true);//2 //等同于 console.log(a + Number(true));//2 console.log([1, 2] + [1, 2]); //1,21,2 //等同于 console.log([1, 2].toString() + [1, 2].toString()); //1,21,2 console.log({} + []);//[object Object] console.log([] + {});//[object Object] console.log({}.toString());//[object Object] console.log([].toString());//""
隱式強制類型轉換為數字,通過一元運算符-、/、*轉換,遵循ToNumber的抽象值操作規則
console.log("3.14" - "0");//3.14 console.log([2] - [1]);//1 //等同于 console.log([2].toString() - [1].toString());//1隱式強制類型轉換為布爾值
以下均遵循ToBolean抽象值操作
if(..)語句中的條件判斷表達式
for(..;..;..)語句的第二個條件判斷表達式
while(..)和do..while(..)的條件判斷表達式
?:中的條件判斷表達式
邏輯運算符||和&&左邊的操作數(a||b等同于a?a:b,a&&b等同于a?b:a)
符號的強制類型轉換Symbol不能被強制轉換為數字(顯式和隱式都會產生錯誤),但可以被強制轉換為布爾值(顯式和隱式結果都為true)
寬松相等和嚴格相等==允許在相等的比較中進行強制類型轉換,===則不能允許,并不是==檢查值是否相等,===檢查值和類型是否相等嚴格相等的兩種特殊情況
NaN不等于NaN;
+0等于-0;
寬松相等之間的隱式轉換1.字符串和數字之間的相等比較
(1) 如果 Type(x) 是數字,Type(y) 是字符串,則返回 x == ToNumber(y) 的結果。
(2) 如果 Type(x) 是字符串,Type(y) 是數字,則返回 ToNumber(x) == y 的結果。
const [a, b] = ["42", 42]; console.log(a == b);//true //等同于 console.log(Number(a) === b);//true console.log(b == a);//true //等同于 console.log(Number(a) === b);//true
2.其他類型和布爾類型之間的比較
(1) 如果 Type(x) 是布爾類型,則返回 ToNumber(x) == y 的結果。
(2) 如果 Type(y) 是布爾類型,則返回 x == ToNumber(y) 的結果。
const [a, b] = [true, 1]; console.log(a == b);//true //等同于 console.log(Number(a) === b);//true console.log(b == a);//true //等同于 console.log(b === Number(a));//true const [c, d] = [false, 0]; console.log(c == d);//true //等同于 console.log(Number(c) === d);//true console.log(d == c);//true //等同于 console.log(d === Number(c));//true console.log("true" == true);//false //等同于 console.log("true" === 1);//false
3.null和undefined之間的相等比較,規范規定null和undefined寬松相等
console.log(null == undefined);//true
4.對象和非對象之間(包括數字、字符串;其中布爾遵循其他類型和布爾類型之間的比較)的相等比較
如果Type(x)是字符串或者數字,Type(y)是對象,則返回x == ToPromitive(y)的結果;
如果Type(x)是對象,Type(y)是字符串或者數字,則返回ToPromitive(x) == y的結果;
const [x, y] = [["42"], 42]; console.log(x == y);//true //等同于 console.log(x.toString() == y);//true const x1 = "abc"; const y1 = new String(x1); console.log(x1 == y1);//true //等同于 console.log(x1 == y1.valueOf());//true
5.一些特殊情況
const [x, y, z] = [undefined, null, NaN]; console.log(x == Object(x) );//false console.log(y == Object(y) );//false //等同于 console.log(x == Object() );//false console.log(y == Object() );//false console.log(z == Object(z) );//false //等同于 console.log(z == new Number(z) );//false //由于Objec(undefined)和Object(null)沒有對應的封裝對象,所以不能夠被封裝, //Objec(undefined)和Object(null)均返回常規對象,等同于Object() //Object(NaN)等同于new Number(NaN), NaN==NaN返回false
6.假值的相等比較
null == "0";//false null == false;//false null == "";//false null == 0;//false undefined == "0";//false undefined == false;//false undefined == "";//false undefined == 0;//false null == undefined;//false //null只會與undefined寬松相等 "0" == false;//true ---特殊 "0" == NaN;//false "0" == 0;//true "0" == "";//false false == NaN;//false false == 0;//true false == "";//true ---特殊 false == [];//true ---特殊 false == {};//false "" == NaN;//false "" == 0;//true ---特殊 "" == [];//true "" == {};//false 0 == NaN;//false 0 == [];//true ---特殊 0 == {};//false 0 == " ";//true ---特殊
7.抽象關系比較>、<、≥、≤
如果雙方都是字符串,則按照字母順序進行比較
如果雙方是其他情況首先調用ToPrimitive轉換為基本類型
如果轉換的結果出現非字符串,則根據ToNumber規則強制轉換為數字進行比較
const a = [42]; const b = ["43"]; console.log(a < b);//true //ToPrimite轉換 console.log(a.toString() < b.toString()); //按照字母順序判斷 console.log("42" < "43");//true const a1 = ["42"]; const a2 = ["043"]; console.log(a1 > a2);//true
關于對象關系比較的奇怪現象
var a = { b: 42 }; var b = { b: 43 }; a < b; // false a == b; // false a > b; // false a <= b; // true a >= b; // true
按理兩邊對象都會進行ToPrimitive抽象值操作,轉換為[object object]應該相等,但是結果卻并非如此,具體原理參考ECMAScript5規范11.8節
8.原理鞏固
如何讓a==2&&a==3?
const a = new Number("something"); let i = 2; Number.prototype.valueOf = ()=>i++; console.log(a == 2 && a == 3);//true
[] == ![]為何為true?
![]首先轉換為false, [] == false符合上面的假值相等
"" == [null]為何為true?
[null]進行ToPrimitive強制類型轉換為""
9.安全運用隱式強制類型轉化
如果兩邊的值中有true或者false,千萬不要使用==
如果兩邊的值中有[]、""或者0,盡量不要使用==
最安全的方式可以通過typeof判斷
最后慣例,歡迎大家star我們的人人貸大前端團隊博客以及個人github,所有的文章還會同步更新到知乎專欄 和 掘金賬號,我們每周都會分享幾篇高質量的大前端技術文章。如果你喜歡這篇文章,希望能動動小手給個贊。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/114897.html
摘要:等同于等同于其他類型和布爾類型之間的比較如果是布爾類型,則返回的結果。 showImg(https://segmentfault.com/img/bVburFq?w=796&h=398); 前言 JavaScript作為一門弱類型語言,我們在每天的編寫代碼過程中,無時無刻不在應用著值類型轉換,但是很多時候我們只是在單純的寫,并不曾停下腳步去探尋過值類型轉換的內部轉換規則,最近通過閱讀你...
摘要:等同于等同于其他類型和布爾類型之間的比較如果是布爾類型,則返回的結果。 showImg(https://segmentfault.com/img/bVburFq?w=796&h=398); 前言 JavaScript作為一門弱類型語言,我們在每天的編寫代碼過程中,無時無刻不在應用著值類型轉換,但是很多時候我們只是在單純的寫,并不曾停下腳步去探尋過值類型轉換的內部轉換規則,最近通過閱讀你...
摘要:強制類型轉換本章介紹了的數據類型之間的轉換即強制類型轉換包括顯式和隱式。強制類型轉換常常為人詬病但實際上很多時候它們是非常有用的。隱式強制類型轉換則沒有那么明顯是其他操作的副作用。在處理強制類型轉換的時候要十分小心尤其是隱式強制類型轉換。 前言 《你不知道的 javascript》是一個前端學習必讀的系列,讓不求甚解的JavaScript開發者迎難而上,深入語言內部,弄清楚JavaSc...
摘要:與此相對,強類型語言的類型之間不一定有隱式轉換。三為什么是弱類型弱類型相對于強類型來說類型檢查更不嚴格,比如說允許變量類型的隱式轉換,允許強制類型轉換等等。在中,加性運算符有大量的特殊行為。 從++[[]][+[]]+[+[]]==10?深入淺出弱類型JS的隱式轉換 本文純屬原創? 如有雷同? 純屬抄襲? 不甚榮幸! 歡迎轉載! 原文收錄在【我的GitHub博客】,覺得本文寫的不算爛的...
閱讀 3557·2021-08-02 13:41
閱讀 2390·2019-08-30 15:56
閱讀 1520·2019-08-30 11:17
閱讀 1174·2019-08-29 15:18
閱讀 580·2019-08-29 11:10
閱讀 2671·2019-08-26 13:52
閱讀 508·2019-08-26 13:22
閱讀 2949·2019-08-23 15:41