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