溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

js的隱式轉(zhuǎn)化是怎樣的

發(fā)布時間:2022-01-18 09:51:12 來源:億速云 閱讀:141 作者:柒染 欄目:開發(fā)技術(shù)

這篇文章將為大家詳細講解有關(guān)js的隱式轉(zhuǎn)化是怎樣的,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關(guān)知識有一定的了解。

前言

之前寫過一篇文章[[js讓人詬病的這些feature]]中提出過一個疑問.

這個問題一開始我想簡單了.認為只要記住一些特性就可以了.所以直接用窮舉法來進行規(guī)律的總結(jié).

但是當遇到console.log(Number([])) 的結(jié)果是0, 而console.log(Number([1, 2])) 的結(jié)果是NaN.都什么亂七八糟的,里面必有蹊蹺.雖然能夠強背背下來, 但是作為一個有追求的程序員還是要弄明白它是怎么一回事的.

console.log({} - {}) // NaN
console.log([] - []) // 0
console.log([] - [1, 2]) // NaN
console.log([] == ![]) // true
console.log({} == {}) // false

要理解上面打印的結(jié)果,就是要理解Number([]) 的值,Number([1, 2])的值, Number({})的值, 以及Boolean([])返回的值. 下面慢慢說道.

一、包裝類

Boolean()

Boolean只有兩種結(jié)果,true和false.

  • Boolean結(jié)果為false的類型,我們通常稱他為 falsey, 中文叫虛值. 這些值在 [[[js讓人詬病的這些feature]] 有所提及,即,

0、null、undefined、false、''、NaN

有些文章把-0和+0算成兩個

上面這些都是原始值轉(zhuǎn)原始值.其他的都是true

如果是引用值轉(zhuǎn)原始值都為true. 還有下面這些一時間想不起來的引用類型.

Boolean(/d/)
Boolean(new Error())
console.log(Boolean(Symbol()))

擴展

還有一種容易弄混的typeof 返回結(jié)果, node環(huán)境中:

console.log(typeof Date())
console.log(typeof new Date()) 
console.log(Date()) // Thu Jan 13 202l2 22:29:36 GMT+0800 (中國標準時間)
console.log(new Date()) // 2022-01-13T14:29:36.660Z

Number()

引用類型轉(zhuǎn)換Number

易錯點出現(xiàn)在Number() 上面. 尤其是引用類型轉(zhuǎn)化為原始類型. 了解了這個,開頭的例子就能夠理解了.

我們只有在知道了Number(引用類型)的規(guī)則才能夠判斷引用類型轉(zhuǎn)化為原始類型的結(jié)果什么,不然是不可能判斷得出的餓,靠猜走不遠.

  • 我們假設(shè)有如下這么一個對象

const obj = {
  toString() {
    return 2	
  },
  valueOf() {
    return 1	
  }
}
  • 我們Number()包裹它

console.log(Number(obj))

打印出的結(jié)果是1. 哦,有意思的來了,

consot obj = {
  toString() {
    return 2	
  },
  valueOf() {
    return {}
  }
}
console.log(Number(obj)) // 2

讓我們讓valueOf 返回的值是對象的時候, 打印出 2 , 反之則直接打印出String原始值,再轉(zhuǎn)化為number類型.

一般來說valueof 就是代表值,沒有意義,也不用處理. 值是什么就是什么,比如說[1, 2, 3].valueOf() 直接打印就是[1, 2, 3].

我們也可以通過這個方法來解決[[讓 a == 1 && a == 2 && a == 3 成立]]的問題.

所以我們很容易得出這么一個規(guī)律: 當valueOf 返回值是引用類型的時候, 就去拿toString 返回的值. 展開來說就是:

  • 如果valueOf返回原始值,就Number包裝之后返回

  • 如果valueOf返回的對象,就去toString()方法中找

  • 如果toString() 返回原始值,就Number包裝之后返回

  • 如果toString()返回的是對象,且是自己重寫的.那么就直接報錯

  • 如果不是充血的,那么就調(diào)用Obejct.prototye.toString方法

這里顯然還涉及到了[[原型鏈]]的問題,所以說其實隱式轉(zhuǎn)化的問題不是想象中的那么簡單的.

而我們創(chuàng)建的對象基本沒講過會創(chuàng)建這兩個方法.所以它很顯然就是繼承至Object上面的方法. 也就是說,我們在研究這個問題的是,就是在研究Object.prototype.toString.call() 返回的值.

console.log(Object.prototype.tostring.call('123'));

console.log(Object.prototype.toString.call(123));

console.log(object.prototype.tostring.call(true));

console.log(Object.prototype.tostring.call(undefined));

console.log(Object.prototype.tostring.call(null));

console.log(Object.prototype.tostring.call(function){}));

console.log(Object.prototype.toString.call([1,2,3]));

console.log(Object.prototype.tostring.call(ff));

打印的結(jié)果如下:

[object string]
[object Number]
[object Boolean]
[object Undefined]
[object Null]
[object Functionl]
[object Arrayl]
[object objectl]

原始類型轉(zhuǎn)Number

console.log(Number(undefined)) // NaN
console.log(Number (null)) // 0
console.log(Number(true)) // 1
console.log(Number (false)) // 0
console.log(Number(NaN)) // NaN
console.1og(Number (Infinity))// Infinity
console.log(Number('') // 0
console.log(Number(' ') // 0
console.log(Number('123')) // 123

上面沒啥好說的, 背下就行. 需要注意的是Number的值,除了我們平時使用的的數(shù)字意義之外,還有NaN、Infinity.

還有這些混淆點是需要注意的:
console.log((123).toString()) // 123
console.log(undefined.toString()) // 報錯
console.log(nul.toString()) // 報錯

undefined 和 null 沒有包裝類, 本身又是基礎(chǔ)類型 ,自然沒有其他亂碼七糟的方法. 所以報錯.

擴展

Argument和document

console.log(Object.toString.call(argument)) // [object Argument]
console.log(Object.toString.call(document)) // [object HTMLDocument]

HTMLDocument是瀏覽器給我們提供的對象類型.

由此也可以得知Object.prototype.toString 方法的運用之廣, 識別類型之多, 比起typeof 簡陋的返回值強大得多. 當然每個都有每個使用的場景就是了.

手寫typeof

typeof是jscore自帶,而且也不是語法糖. 我一開始看到這個面試題的時候是懵逼的. 難道要手寫typeof的引擎代碼? 解釋一下從js第一個版本就存在的typeof null為object嗎?

但是還真有公司考這個, 有點睿智,大聰明. 所以我看了看網(wǎng)上別人對于這的解析... 就是利用Object.prototype.toString.call()返回的結(jié)果,在進行字符串的切割,之后后面那個單詞返回出去.

就這? 脫褲子放屁,多此一舉.

Number轉(zhuǎn)化非二進制

Number(0xfff) // 4095
Number(070) // 56

Number可以直接識別不同位數(shù)轉(zhuǎn)化成十進制.

parseInt和Number關(guān)系

Number('123abc') // NaN
Number('ad123') // NaN
Number('  123') // 123

而[[parseInt]]就很好的解決了這個問題,它可以說是對于Number()很好的一個補充

parseInt('123abc') // 123
parseInt('123asd1') // 123
parseInt('ad123') // NaN
parseInt('  123') // 123

String()

Object.prototype.toString

對于String()的使用依舊使用Number()使用的例子

const obj = {
  toString() {
    return 2	
  },
  valueOf() {
    return 1	
  }
}

當我觸發(fā)String(obj)的時候,就和Number()完全相反.

console.log(String(obj)) // 2

直接訪問的是toString()方法.

const obj = {
  toString() {
    return {}
  },
  valueOf() {
    return 1	
  }
}

console.log(String(obj)) // 1

但是如果toString() 返回的是引用類型的話, 就往valueOf()方法上面找. 可以說和Number()的完全相反,但是也符合情理 .

通過重寫toString()和valueOf()的方法來了解內(nèi)部的運行規(guī)則是一種很好的方式.

如果不重寫的話,Object.prototype.toString.call(對象), 返回值參看Number()部分的內(nèi)容.

console.log(String({})) // [object Object]
Array.prototype.toString

這個記憶上沒啥好說的, 直接把外面的[]給拆了就行.

console.log(String([1])) // '1'
console.log(String([1, 2])) // '1, 2'
console.log(Array.prototype.toString.call([1])) // '1'
console.log(Array.prototype.toString.call([1, 2])) // '1, 2'

二、隱式轉(zhuǎn)化觸發(fā)規(guī)則

前面說了顯示轉(zhuǎn)化的規(guī)律. 下面是能夠觸發(fā)隱式轉(zhuǎn)化的規(guī)則.

和運算符規(guī)則是和[[運算符的優(yōu)先級]],在這里不提,可自行查閱.

布爾的隱式轉(zhuǎn)化

當出現(xiàn)判斷的時候,會出現(xiàn)隱式轉(zhuǎn)化.

if,
switch,
while,
for(; ;),
&&,
||,
!,
!!,
? : 三元

number的隱式轉(zhuǎn)化

只要有小學的知識都知道運算符,它是用于數(shù)字之間的計算的.在JavaScript中也是基本是一樣的.

+ - * % == ~~ & | ~ ^ << <<< 等, 位運算符 、算術(shù)運算符

隱式轉(zhuǎn)化最難的情況

== !== >= <= < >

如下例子, 也是面試的高頻題目,背下來之余,還是要知道得到這樣結(jié)果的過程.

console.log([] == ![]) // true

個人覺得隱式轉(zhuǎn)化最復(fù)雜的就是這個例子了.再復(fù)雜大不了加上優(yōu)先級. 回到這個例子中, 看似比較的是兩個數(shù)組,或者說兩個特殊對象.其實不全是.來解析這個例子:

  • 看到 等號 這個比較運算法就應(yīng)該明白 等號 兩邊都要轉(zhuǎn)化成Number類型

  • 從左到右的話,Number([]), [] 是引用類型,無法直接拿到原始值

  • valueOf拿不到值,就走Array.prototype.toString.call([]).從上面可以知道, 它返回的是去掉[],即字符串''.

  • 此時左邊為Number(''). 所以左邊返回的自然是0.

此時這個題目為0 == ![]. 接下來右邊的轉(zhuǎn)化,這就簡單了.

  • 在Boolean()一節(jié)當中,就可以知道,除了falsey之外,其他都是ture.而此時在!的加持下,[]會進行Boolean()

  • 此時右邊為true. !true就為false

  • Number(false)的結(jié)果為0

由上而得出 0 == 0 的結(jié)果為true.運用上面的知識點可以很好的解析問題,下面的這個例子就更加簡單了.

console.log({} == {}) // false
console.log({} != {})

如果按照一樣的分析方式來解釋的話:

  • 兩邊都Number()包裹住.

  • toString()之后都是[obejct Object]

  • Number('[obejct Object]') 為NaN

  • 所以最后轉(zhuǎn)化為console.log(NaN == NaN)的比較

NaN和任何一個值比較都不想等

題目不難,但是綜合的東西還是有一點的. 這兩題解決了,隱式轉(zhuǎn)化的問題也就到頭了

三、特殊情況

最容易記住的就是字符串運算符.

console.log(1 + '2' + '2') // '122'
console.log(1 + + '2' + '2') // '32'
console.log('A' - 'B' + '2') // 'NaN2'
console.log('A' - 'B' + 2) // NaN

js 字符串和任何數(shù)據(jù)類型想加都轉(zhuǎn)化為字符串么?可以這么說,處理symbol類型直接報錯之外.

console.log(typeof (+ '2')) // number

還有下面undfined和null的特殊情況

console.log(undefined == null) // true
console.log(NaN == NaN) // fasle
  • NaN的語意是not a number,很明顯了,指的就不是一個數(shù)字

  • NaN在typeof中是number類型,但是它和任何數(shù)都不想.

關(guān)于js的隱式轉(zhuǎn)化是怎樣的就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

js
AI