JS進(jìn)階 | 堆棧內(nèi)存篇之?dāng)?shù)據(jù)類型與類型轉(zhuǎn)換

堆棧內(nèi)存篇之?dāng)?shù)據(jù)類型與類型轉(zhuǎn)換

前言

堆棧內(nèi)存篇將以JavaScript的數(shù)據(jù)類型為引,然后一步步刨析堆棧內(nèi)存平挑,以此來(lái)了解JS的運(yùn)行機(jī)制游添。在這個(gè)過(guò)程中,我們也會(huì)學(xué)習(xí)到一些經(jīng)常出現(xiàn)的面試知識(shí)點(diǎn)弹惦,比如this指向否淤、閉包等。

數(shù)據(jù)類型分類

JS的數(shù)據(jù)類型分為兩大類棠隐,一類是基本數(shù)據(jù)類型石抡,一類是引用數(shù)據(jù)類型。兩種數(shù)據(jù)類型最大的區(qū)別在于:基礎(chǔ)數(shù)據(jù)類型是按值訪問的(實(shí)際操作的是值本身)助泽;而引用數(shù)據(jù)類型是按引用訪問的(實(shí)際操作的是地址)啰扛。

基本數(shù)據(jù)類型一共有七種:StringBoolean嗡贺,Number隐解,BigintSymbol诫睬,Null煞茫,Undefinded

引用數(shù)據(jù)類型有很多種,常見的有:ObjectArray续徽,Function蚓曼,DateRegExp等钦扭。

引用數(shù)據(jù)類型可以統(tǒng)稱為一種纫版,即Object,因?yàn)樵贘S中客情,一切都是基于對(duì)象的其弊。所以也有一些書籍在介紹JS的數(shù)據(jù)類型分類時(shí),是介紹為一共有八種數(shù)據(jù)類型膀斋,包括七種基本數(shù)據(jù)類型和一種復(fù)雜數(shù)據(jù)類型的梭伐。

我們?cè)谄匠J褂玫倪^(guò)程中可能會(huì)疑惑:為什么StringBoolean概页,Number是基本數(shù)據(jù)類型籽御,但卻擁有其他引用類型一樣的特點(diǎn),比如:它們的實(shí)例同樣擁有屬性方法惰匙。這是因?yàn)檫@三個(gè)基本數(shù)據(jù)類型是極其特殊的,它們被稱為原始值包裝類型铃将。在每次用到某個(gè)原始值的屬性或方法時(shí)项鬼,后臺(tái)都會(huì)創(chuàng)建一個(gè)相對(duì)應(yīng)的對(duì)象,以此來(lái)暴露那些屬性方法劲阎,對(duì)于用戶來(lái)說(shuō)绘盟,就像在操作引用數(shù)據(jù)類型一樣。

let str1 = 'this is a test'
let str2 = str1.substring(2)

比如以上的代碼悯仙,當(dāng)執(zhí)行到第二句的時(shí)候龄毡。后臺(tái)執(zhí)行了以下三步:1.創(chuàng)建一個(gè)String類型的實(shí)例;2.調(diào)用實(shí)例上的特定方法锡垄;3.銷毀創(chuàng)建的實(shí)例

可以注意到這里創(chuàng)建的實(shí)例對(duì)象在執(zhí)行完語(yǔ)句之后就被銷毀了沦零,這也是引用類型和原始值包裝類型的不同之處:引用類型實(shí)例化的對(duì)象在離開作用域時(shí)才被銷毀,而原始值包裝類型自動(dòng)實(shí)例化的對(duì)象只存在于訪問它的那行代碼的執(zhí)行期間货岭,當(dāng)這行代碼運(yùn)行結(jié)束路操,該對(duì)象就被銷毀了。這意味著:在運(yùn)行時(shí)給和原始值包裝類型的變量添加屬性和方法是無(wú)效的

let str1 = 'test test'
str1.name = 'str1'
console.log(str1.name) // undefined

類型檢測(cè)

typeof

typeof操作符是類型檢測(cè)最基礎(chǔ)的方法千贯,對(duì)一個(gè)變量使用typeof操作符時(shí)屯仗,將會(huì)返回其對(duì)應(yīng)的數(shù)據(jù)類型

console.log(typeof 'string') // string
console.log(typeof 123) // number
console.log(typeof true) // boolean
console.log(typeof 123n) // bigInt
console.log(typeof Symbol()) // symbol
console.log(typeof a) // undefined
console.log(type null) // object

console.log(typeof new Object()) // object
console.log(typeof new Array()) // objecct
console.log(typeof new Function()) //function

注意:基本數(shù)據(jù)類型中除了null之外都會(huì)返回對(duì)應(yīng)的數(shù)據(jù)類型,而null返回的是object搔谴,這是因?yàn)?code>null表示的是一個(gè)空對(duì)象魁袜,因此是一個(gè)對(duì)象。而引用數(shù)據(jù)類型中除了function之外都返回object,因?yàn)?code>function雖然也是對(duì)象峰弹,但因?yàn)槠涮厥庑缘炅浚跃桶阉推渌麑?duì)象區(qū)分開來(lái)。

instanceof

由上可知垮卓,當(dāng)對(duì)引用數(shù)據(jù)類型使用typeof時(shí)垫桂,除了function之外,其他的都是返回object粟按,這就不能使用它來(lái)區(qū)分Array诬滩,Date等類型了。

instanceof的功能就是用來(lái)判斷一個(gè)對(duì)象是否為另一個(gè)構(gòu)造函數(shù)(類)的實(shí)例灭将。

語(yǔ)法為object instanceof constructor疼鸟。該運(yùn)算符是通過(guò)判斷對(duì)象的原型鏈上是否存在著構(gòu)造函數(shù)的原型對(duì)象(本文不著重介紹關(guān)于原型鏈和原型對(duì)象的細(xì)節(jié)),當(dāng)結(jié)果為是時(shí)返回true庙曙,否則返回false

let a = []
a instanceof Array // true
a instanceof Object // true
a instanceof String // false

且該修飾符不局限于JS自帶的引用數(shù)據(jù)類型空镜,它也可用于自定義的構(gòu)造函數(shù)

function Foo() {} // 自定義的構(gòu)造函數(shù)
let bar = new Foo() 
bar instanceof Foo // true
bar instanceof object // true

constructor

我們知道原始值包裝類型使用起來(lái)與引用數(shù)據(jù)類型并沒有多大不同,其在被訪問其屬性和方法時(shí)也會(huì)進(jìn)行實(shí)例化一個(gè)對(duì)象捌朴。因此使用字面量方式和構(gòu)造函數(shù)創(chuàng)建的基本數(shù)據(jù)類型也有著不同

let a = 123
let b = Number(321)

a instanceof Number // false
b instanceof Number // true

當(dāng)遇到這種問題時(shí)吴攒,就可以使用constroctor來(lái)進(jìn)行檢測(cè)

let a = 123
let b = Number(321)

console.log(a.constructor === Number)// true
console.log(b.constructor === Number)// true

但這個(gè)方法也不是最優(yōu)解,畢竟constructor砂蔽,__proto__等屬性是可能會(huì)被改寫的

Object.prototype.toString.call()

該方法是目前最常用也是最準(zhǔn)確的方式洼怔。

每一個(gè)對(duì)象都有著一個(gè)toString方法,當(dāng)該方法沒有被重寫時(shí)左驾,其執(zhí)行結(jié)果返回[object type]镣隶,其中type表示對(duì)象的類型。而許多數(shù)據(jù)類型為了實(shí)現(xiàn)特定的功能都對(duì)其進(jìn)行了重寫诡右,比如Array安岂,StringDate等帆吻,所以在進(jìn)行類型檢測(cè)時(shí)域那,都會(huì)使用Object.prototype.toString.call(object)的方式,通過(guò)改寫其this的綁定桅锄,來(lái)調(diào)用Object原型上的toString

let a = [1,2,3,4]
console.log(a.toString()) // "1,2,3,4"
console.log(Object.prototype.toString.call(a)) // "[object Array]"

類型轉(zhuǎn)換

在一些情況下琉雳,數(shù)據(jù)類型之間會(huì)發(fā)生自動(dòng)轉(zhuǎn)換。比如在流程控制語(yǔ)句之中友瘤,+性運(yùn)算符翠肘,==運(yùn)算符等。JS也提供了一些函數(shù)方法用于主動(dòng)轉(zhuǎn)換類型辫秧。

轉(zhuǎn)為布爾類型

JS提供了Boolean()轉(zhuǎn)型函數(shù)來(lái)進(jìn)行其他類型到布爾類型的轉(zhuǎn)變束倍。

下表為不同類數(shù)據(jù)類型與布爾類型的轉(zhuǎn)換規(guī)則:

數(shù)據(jù)類型 轉(zhuǎn)為true 轉(zhuǎn)為false
Boolean true false
String 非空字符串 空字符串
Number 非0數(shù)值 0、NaN
Object 任意對(duì)象 null
Undefined N/A undefined

轉(zhuǎn)為數(shù)字類型

JS提供了三個(gè)函數(shù)來(lái)將非數(shù)值轉(zhuǎn)換為數(shù)值:Number()parseInt()绪妹,parseFloat()甥桂。

Number()是主要的轉(zhuǎn)型函數(shù),可用于任何數(shù)據(jù)類型邮旷。而后兩個(gè)函數(shù)主要用于將字符串轉(zhuǎn)為數(shù)值黄选。

參數(shù)數(shù)據(jù)類型 Number() parseInt() parseFLoat()
布爾值 true 轉(zhuǎn)換為 1,false 轉(zhuǎn)換為 0 返回NaN 返回NaN
數(shù)值 直接返回 直接返回 直接返回
null 返回 0 返回NaN 返回NaN
undefined 返回 NaN 返回 NaN 返回 NaN
字符串 只包含數(shù)值字符(前面帶正負(fù)號(hào)的數(shù)婶肩、浮點(diǎn)型办陷、有效的十六進(jìn)制)轉(zhuǎn)化成相應(yīng)的數(shù)值;空字符串返回0律歼;包含上述情況之外的字符返回NaN 與Number()不同點(diǎn):空字符串返回NaN;只要第一個(gè)字符是數(shù)值字符民镜、加號(hào)、減號(hào)险毁,則會(huì)依次檢測(cè)每個(gè)字符(即后面包含其他字符也會(huì)返回前面的數(shù)值) 與parseInt()基本相同制圈,但只有第一個(gè)小數(shù)點(diǎn)有效
對(duì)象 調(diào)用 valueOf()方法,并按照上述規(guī)則轉(zhuǎn)換返回的值畔况。如果轉(zhuǎn)換結(jié)果是 NaN鲸鹦,則調(diào)用 toString()方法,再按照轉(zhuǎn)換字符串的規(guī)則轉(zhuǎn)換跷跪。 調(diào)用 valueOf()方法亥鬓,并按照上述規(guī)則轉(zhuǎn)換返回的值。如果轉(zhuǎn)換結(jié)果是 NaN域庇,則調(diào)用 toString()方法,再按照轉(zhuǎn)換字符串的規(guī)則轉(zhuǎn)換覆积。 調(diào)用 valueOf()方法听皿,并按照上述規(guī)則轉(zhuǎn)換返回的值。如果轉(zhuǎn)換結(jié)果是 NaN宽档,則調(diào)用 toString()方法尉姨,再按照轉(zhuǎn)換字符串的規(guī)則轉(zhuǎn)換。
// 1.布爾值
Number(true) // 1
parseInt(true) // NaN
parseFloat(true) // NaN

// 2.數(shù)值
Number(123) // 123
parseInt(123) // 123
parseFloat(123.321) //123.321

// 3.null
Number(null) // 0
parseInt(null) // NaN
parseFloat(null) // NaN

// 4.undefined
Number(undefined) // NaN
parseInt(undefined) // NaN
parseFloat(undefined) // NaN

// 5.字符串
let str = '123abc'
Number(str) // NaN
parseInt(str) // 123
parseFloat(str) // 123
let str1 = ''
Number(str1) // 0
parseInt(str1) // NaN
parseFloat(str1) // NaN

// 6.對(duì)象
let a = [1,2,3]
a.valueOf() // [1,2,3]
a.toString() // "1,2,3"
Number(a) // NaN,因?yàn)榘ǘ禾?hào)
parseInt(a) // 1
parseFloat(a) // 1

Number()和其他兩個(gè)函數(shù)最主要的區(qū)別在于處理字符串時(shí)不同吗冤。比如轉(zhuǎn)換空字符串時(shí)又厉,Number()返回的是0,而其他兩個(gè)則返回NaN椎瘟。而且當(dāng)?shù)谝粋€(gè)字符為數(shù)字覆致,且后面包括其他字符時(shí),Number()則直接返回NaN肺蔚,其他兩個(gè)則會(huì)忽略其他字符煌妈。

而因?yàn)檗D(zhuǎn)換字符串時(shí)的區(qū)別所以也間接導(dǎo)致轉(zhuǎn)換對(duì)象時(shí)的不同。

轉(zhuǎn)為字符串類型

JS提供了toString()String()來(lái)將其他類型轉(zhuǎn)換為字符串。

之前我們介紹類型轉(zhuǎn)換時(shí)璧诵,已經(jīng)介紹過(guò)了toString()汰蜘,則是每一個(gè)對(duì)象都有的方法,但可能經(jīng)過(guò)了重寫之宿。而且還有一個(gè)小操作:在對(duì)數(shù)值調(diào)用該方法時(shí)族操,可以傳入一個(gè)參數(shù)來(lái)以什么進(jìn)制來(lái)輸出數(shù)值的字符串表示。

但并不是所有對(duì)象都有toString()比被,比如null色难,undefined。此時(shí)就只能使用String()轉(zhuǎn)型函數(shù)姐赡。當(dāng)不確定一個(gè)值是否為nullundefined時(shí)莱预,使用該方法將會(huì)遵循以下規(guī)則:1.如果值有toString(),則直接調(diào)用 2.如果值為null项滑,則返回'null' 3.如果值為undefined依沮,則返回'undefined'

let value1 = 10; 
let value2 = true; 
let value3 = null; 
let value4;

console.log(String(value1)); // "10" 
console.log(String(value2)); // "true" 
console.log(String(value3)); // "null" 
console.log(String(value4)); // "undefined"

加性操作符

當(dāng)兩個(gè)操作數(shù)都為數(shù)值時(shí),則進(jìn)行加法運(yùn)算枪狂。

當(dāng)其中一個(gè)操作數(shù)為字符串時(shí)危喉,若另一個(gè)也為字符串則進(jìn)行拼接,否則則將另一個(gè)操作數(shù)轉(zhuǎn)換為字符串州疾,然后拼接辜限。

console.log(1 + 1) // 2
console.log(1 + '1') // 11
console.log('1' + { a:123 }) // 1[object Object]

等于與不等于操作符

當(dāng)任意一個(gè)操作數(shù)為布爾類型,則將其轉(zhuǎn)為數(shù)值再進(jìn)行比較严蓖。

當(dāng)一個(gè)操作數(shù)為字符串薄嫡,另一個(gè)為數(shù)值時(shí),則將字符串轉(zhuǎn)為數(shù)值再進(jìn)行比較颗胡。

當(dāng)一個(gè)操作數(shù)為對(duì)象毫深,另一個(gè)不是,則使用對(duì)象的valueOf()取值毒姨,然后再進(jìn)行比較哑蔫。

如果兩個(gè)操作數(shù)都是對(duì)象,則比較它們是不是同一個(gè)對(duì)象弧呐。

null等于undefined闸迷,兩者都不能轉(zhuǎn)為其他類型。

當(dāng)任意一個(gè)操作數(shù)為NaN俘枫,則相等操作符返回 false腥沽,不相等操作符返回 true。因?yàn)榧词箖蓚€(gè)操作數(shù)都是 NaN崩哩,相等操作符也返回 false巡球,因?yàn)榘凑找?guī)則言沐,NaN 不等于 NaN。

流程控制語(yǔ)句

在JS的流程控制語(yǔ)句中酣栈,比如if险胰,whiledo-while之中矿筝。其條件(可能是表達(dá)式或數(shù)據(jù)類型)的結(jié)果不必都為布爾類型起便,因?yàn)樵趦?nèi)部會(huì)自動(dòng)將其值轉(zhuǎn)換為布爾類型(調(diào)用Boolean()方法)

堆棧內(nèi)存

上面我們介紹了數(shù)據(jù)類型分為基本數(shù)據(jù)類型和引用數(shù)據(jù)類型,其最大的區(qū)別在于訪問方式窖维。那為什么會(huì)有這種不同呢榆综?

首先我們需要知道JS的運(yùn)行的內(nèi)存主要分為棧內(nèi)存和堆內(nèi)存。棧內(nèi)存即執(zhí)行棧铸史,是任務(wù)主要的執(zhí)行環(huán)境鼻疮。但因?yàn)闂5目臻g有限,不能用來(lái)直接保存對(duì)象琳轿,所以就使用了堆內(nèi)存用來(lái)保存真正的對(duì)象判沟,然后在棧上保存指向堆內(nèi)存中對(duì)應(yīng)的地址,該地址即為引用崭篡。

因此可知:基本數(shù)據(jù)類型是保存在棧上的挪哄,操作時(shí)是對(duì)實(shí)際的值進(jìn)行操作;而引用數(shù)據(jù)類型是保存在堆上的琉闪,對(duì)其進(jìn)行操作時(shí)實(shí)際上是通過(guò)地址(引用)來(lái)對(duì)堆內(nèi)存對(duì)應(yīng)區(qū)域內(nèi)的對(duì)象進(jìn)行操作迹炼。

其示意圖如下,關(guān)于執(zhí)行上下文的內(nèi)容將在下一篇文章進(jìn)行介紹颠毙。


1605098797264.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斯入,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蛀蜜,更是在濱河造成了極大的恐慌咱扣,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涵防,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡沪铭,警方通過(guò)查閱死者的電腦和手機(jī)壮池,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杀怠,“玉大人椰憋,你說(shuō)我怎么就攤上這事∨馔耍” “怎么了橙依?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵证舟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我窗骑,道長(zhǎng)女责,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任创译,我火速辦了婚禮抵知,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘软族。我一直安慰自己刷喜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布立砸。 她就那樣靜靜地躺著掖疮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪颗祝。 梳的紋絲不亂的頭發(fā)上浊闪,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天,我揣著相機(jī)與錄音吐葵,去河邊找鬼规揪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛温峭,可吹牛的內(nèi)容都是我干的猛铅。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼凤藏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼奸忽!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起揖庄,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤栗菜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蹄梢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疙筹,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年禁炒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了而咆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幕袱,死狀恐怖暴备,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情们豌,我是刑警寧澤涯捻,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布浅妆,位于F島的核電站,受9級(jí)特大地震影響障癌,放射性物質(zhì)發(fā)生泄漏凌外。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一混弥、第九天 我趴在偏房一處隱蔽的房頂上張望趴乡。 院中可真熱鬧,春花似錦蝗拿、人聲如沸晾捏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惦辛。三九已至,卻和暖如春仓手,著一層夾襖步出監(jiān)牢的瞬間胖齐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工嗽冒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留呀伙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓添坊,卻偏偏與公主長(zhǎng)得像剿另,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贬蛙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361