javascript數(shù)據(jù)類型之原始類型詳解
javascript的數(shù)據(jù)類型
開辟了一個關(guān)于javascript的基礎(chǔ)系列,更加深入、細(xì)致的了解這門語言。今天分享的是js的數(shù)據(jù)類型。
javascript的數(shù)據(jù)類型可以分為兩類:原始類型(基礎(chǔ)數(shù)據(jù)類型)
和對象類型(引用數(shù)據(jù)類型)
原始類型包括:數(shù)字
、字符串
、布爾值
、以及特殊的undefined
和null
除了以上的數(shù)據(jù)類型,其他就都是對象類型了
具有代表性的對象類型有:對象(object)
、數(shù)組(array)
、函數(shù)(function)
本次我們著重介紹原始數(shù)據(jù)類型
兩個小注意點(diǎn):
1.js語言是弱類型語言(并不是沒有數(shù)據(jù)類型)
2.在js語言中所聲明的變量是沒有數(shù)據(jù)類型的,因此可以被賦予任何類型的值
原始類型(基礎(chǔ)數(shù)據(jù)類型)
數(shù)字
和其他變成語言不同,js不區(qū)分正整數(shù)值和浮點(diǎn)數(shù)值,js中所有數(shù)字都是用浮點(diǎn)數(shù)值表示的。
數(shù)字的算術(shù)運(yùn)算符方法有+
,-
,*
,/
,%
(加,減,乘,除,余)。
除此之外,js還支持更復(fù)雜的算術(shù)運(yùn)算,這些復(fù)雜運(yùn)算通過作為Math對象的屬性定義和常量來實(shí)現(xiàn):
// 2的53次冪 Math.pow(2, 53) // 0.6的四舍五入值 Math.round(0.6) // 向上取整 Math.ceil(0.6) // 向下取整 Math.floor(0.6) // 取絕對值 Math.abs(-5) // 求出x,y,z的最大值 Math.max(x, y, z) // 求出x,y,z的最小值 Math.min(x, y, z) // 生成一下大于等于0小于1的隨機(jī)數(shù) Math.random() // 圓周率 Math.PI // e自然對數(shù)的底數(shù) Math.E // 3的開平方根 Math.sqrt(3) // 3的開立方根 Math.pow(3, 1/3) // 三角函數(shù) Math.sin(0) // 求10的自然對數(shù) Math.log(10) // 以10為底數(shù)的100的對數(shù) Math.log(100)/Math.LN10 // 以2為底數(shù)的512的對數(shù) Math.log(512)/Math.LN2 // e的3次方冪 Math.exp(3)
js的數(shù)字表示范圍是有限制的(能否表示的限制、能否滿足精度到個位的限制以及能否作為數(shù)組索引的限制)
具體的情況如下圖:
(圖片來自網(wǎng)絡(luò),侵刪)
因此javascript在進(jìn)行數(shù)學(xué)運(yùn)算時,會出現(xiàn)溢出和下溢兩種情況,
溢出的情況為:
當(dāng)運(yùn)算結(jié)果超出了js語言所能表示的上線(即圖中1.8e308
—正無窮
的區(qū)域),結(jié)果會返回Infinity
(表示無窮大)
同樣的,當(dāng)計(jì)算的負(fù)數(shù)的值超過了能表示的負(fù)數(shù)范圍(即圖中-1.8e308
—負(fù)無窮
的區(qū)域),結(jié)果會返回-Infinity
(表示負(fù)無窮大)
下溢的情況為:
當(dāng)運(yùn)算的結(jié)果無限接近于0,并比js能表示的最小值還小的情況(即圖中0
—5e-324
的區(qū)域)。這樣結(jié)果會返回0
。
同樣的,當(dāng)一個負(fù)數(shù)發(fā)生下溢(即圖中0
—-5e-324
的區(qū)域),這時結(jié)果會返回一個-0
。
上文,我們介紹數(shù)字中預(yù)定義的全局變量Infinity
,此外還有一個預(yù)定義的全局變量NaN
(表示非數(shù)字,not-a-number,當(dāng)運(yùn)算的結(jié)果并不是一個數(shù)字值的時候,會返回NaN
)
在js中,NaN有特殊的一點(diǎn),就是它和任何值都不相等(包括自身),因此想要判斷一個值是否為NaN
,可以使用x != x
判斷
var x = 1 - 'a' x != x //true
除此之外,我們還可以調(diào)用全局預(yù)定好的函數(shù)isNaN
// 當(dāng)傳入的參數(shù)只要不是一個數(shù)字,就返回true isNaN(5 - 'a') // true isNaN('1') // true isNaN('a') // true isNaN({a: 2}) // true isNaN(1) //false isNaN(Infinity) //false
另外,全局還有一個預(yù)定好的函數(shù)isFinite
// 當(dāng)傳入的參數(shù)只要不是NaN, Infinity, -Infinity就返回true isFinite(5 - 'a') // false isFinite('1') // false isFinite('a') // false isFinite({a: 2}) // false isFinite(1) // true isFinite(Infinity) //false
數(shù)學(xué)中實(shí)數(shù)有無限多個,而在javascript語言中能通過浮點(diǎn)數(shù)的形式只能表現(xiàn)其中的有限個,因此在js中使用實(shí)數(shù)的時候,我們往往都是使用的一個近似值。
javscript所采用的浮點(diǎn)數(shù)表示發(fā),是一種二進(jìn)制表示法,因此我們可以精確的表示1/2
、1/8
、1/1024
。但是在數(shù)學(xué)中,我們常用的都是十進(jìn)制分?jǐn)?shù)1/10
。所以js中并不能精確的表示像0.1
這樣簡單的數(shù)字。
var x = 0.3-0.2 var y = 0.2-0.1 x == y // false
因此要避免在js中用浮點(diǎn)數(shù)進(jìn)行計(jì)算(盡量使用整數(shù))
文本
javascript中的字符串采用的是UTF-16編碼的Unicode字符集
,字符串的長度是其含有16位值的個數(shù),如下:
var a = 'z' var b = '?' // 注意,這個字不是“吉祥”的吉 a.length // => 1: a包含的一個16位值 \u007A b.length // => 2: b包含兩個16位值 \uD842\uDFB7
在js語言中,字符串是由單引號或雙引號括起來的字符序列,定義的由單引號定界的字符串中可以包含雙引號,同樣,定義的由雙引號定界的字符串中也可以包含單引號。
字符串可以拆分為數(shù)行,每行必須以\
結(jié)束,如果希望在字符串中再起一行可以使用轉(zhuǎn)義字符\n
全部的轉(zhuǎn)義字符如下:
測試輸出結(jié)果如下:
但是,在ES6中,新增了模板字符串,模板字符串是用反勾號`
將字符括起
在模板字符串中換行就簡單很多:
` hello world ` // 等價(jià)于 'hello\nworld'
除此之外,模板字符串還支持元素注入
var str = 'world' `hello ${world}` // 等價(jià)于 'hello ' + str
除了字符串的length屬性之外,字符串還有很多可以調(diào)用的方法
var str = 'Hello, World' str.charAt(0) // H, 返回第一個位置的字符 str.charAt(s.length - 1) // t, 返回最后一個位置的字符 str.substring(1,4) // ell, 返回位置2-4的字符 str.slice(1,4) // ell, 同上 str.slice(-3) // rld, 返回最后三個字符 str.indexOf('l') // 2, 返回首次出現(xiàn)l的位置 str.lastIndexOf('l') // 10,返回最后一次出現(xiàn)l的位置 str.split(", ") // ['Hello', 'World'], 分割為數(shù)組 str.replace('H', 'h') // 'hello, World', 將h替換為H str.toUpperCase() // 'HELLLO, WORLD', 將字符串所有字母變?yōu)榇髮? str.toLowerCase() // 'hello, world', 將字符串所有字母變?yōu)樾? // es6新增方法 let s = 'Hello world!'; s.startsWith('Hello') // true s.endsWith('!') // true s.includes('o') // true // includes():返回布爾值,表示是否找到了參數(shù)字符串。 // startsWith():返回布爾值,表示參數(shù)字符串是否在原字符串的頭部。 // endsWith():返回布爾值,表示參數(shù)字符串是否在原字符串的尾部。 'x'.repeat(3) // "xxx" 'hello'.repeat(2) // "hellohello" 'na'.repeat(0) // "" // repeat方法返回一個新字符串,表示將原字符串重復(fù)n次
需要注意的是,對于字符串的任何方法都會返回一個新的字符串,而不會在原字符串上修改。
布爾值
javascript中布爾值有兩個true
和false
null&undefined
null和undefined都表示”值的空缺“,但事從背后更深遠(yuǎn)的角度考慮,他們的還是有差別的。
對null進(jìn)行typeof
檢測,返回值是object
對undefined進(jìn)行typeof
檢測,返回值是undefined
undefined表示,對這個值還未定義,還沒有進(jìn)行初始化。比如,當(dāng)我們聲明一個變量,但是卻未賦值,此時會返回undefined,當(dāng)我們獲取一個對象未定義的屬性,此時會返回undefined,當(dāng)我們調(diào)用一個函數(shù),卻未傳參,參數(shù)會返回undefined。
null表示,沒有對象,此處沒有值,此處不應(yīng)該有值。比如,原型鏈的重點(diǎn)就是null。
后面會從棧
和堆
的角度進(jìn)行另一番解釋。
我們可以這么理解,undefined是系統(tǒng)級的、出乎意料的、類似錯誤的空缺。而null是程序級的、正常的、在意料之中的值的空缺。在某些場景下,比如想賦值給一個變量,想表示變量為空,或作為參數(shù)傳入一個函數(shù),這是,最佳的選擇是null。
包裝對象
了解包裝對象之前,我們首先思考這么一個問題。
var a = 'test' a.length //4
我們知道上面代碼中的a
是一個字符串,字符串不是一個對象,不能進(jìn)行.
關(guān)鍵字的操作。但是,為什么我們可以得到a.length
呢?
因?yàn)橹灰嬖诎b對象的概念,在上述代碼執(zhí)行的過程中,js會將字符串通過new String
的方式生成一個包裝對象,這個對象繼承了String
的方法,因?yàn)榭梢酝ㄟ^.
的方式訪問到。一旦屬性的引用結(jié)束,這個包裝對象就會被銷毀(其實(shí)在js語言內(nèi)部的實(shí)現(xiàn)上不一定創(chuàng)建或銷毀這個對象,但是整個過程在執(zhí)行層面看起來是這樣的,我們也可以這么進(jìn)行理解)
原始類型和引用類型的變與不變關(guān)系
想要深入理解原始類型和引用類型的變與不變,相等比較等問題的時候,我們需要借助棧
和堆
的思想來理解,我們可以這么思考:
(圖片來自網(wǎng)絡(luò),侵刪)
這張圖闡述了原始類型和引用類型的關(guān)系:
原始類型保存在棧內(nèi)存中,原始類型(包括字符串、數(shù)字、布爾型、undefined)是保存在棧內(nèi)存中,是不可以修改的(我們所看到的修改,其實(shí)都是刪除后重新賦值),當(dāng)復(fù)制一個原始類型的時候,其實(shí)就是在內(nèi)存中復(fù)制這么值。其中,undefined代表的就是未被賦值的一個棧內(nèi)存的區(qū)域。
引用類型保存在堆內(nèi)存中,但是在棧內(nèi)存中存了一個引用類型的地址,棧內(nèi)存中的地址有一個指針指向堆內(nèi)存的引用類型。這個引用類型是可以進(jìn)行修改的,比如我們可以向數(shù)組中push一個新值。如果我們只是簡單的復(fù)制一個引用類型(淺拷貝),那么其實(shí)復(fù)制的是這個在棧內(nèi)存中的地址,復(fù)制后的值發(fā)生修改,那么之前被復(fù)制的值也同樣會被修改,因此在復(fù)制引用類型的時候,最好要進(jìn)行深拷貝。其中,null很特殊,表示的是在棧內(nèi)存中,有一個指針指向堆內(nèi)存中的引用類型,一旦這個指針掉了,就是null。
類型轉(zhuǎn)換
jacascript中的類型轉(zhuǎn)換非常常見,也是javascript語言中非常重要的一點(diǎn)。
首先我們來看一下類型轉(zhuǎn)化表:
任意JavaScript的值都可以轉(zhuǎn)換為布爾值,只有undefine、null、0、NaN、""會被轉(zhuǎn)換為false,其他所有值都會被轉(zhuǎn)換成true。
當(dāng)字符串轉(zhuǎn)化為數(shù)字?jǐn)?shù)字時,那些數(shù)字表示的字符串可以轉(zhuǎn)化為數(shù)字,也允許在開始和結(jié)尾處有空格,但是其他含有非空非數(shù)字字符都不會完成到數(shù)字的轉(zhuǎn)化,他們會轉(zhuǎn)化為NaN。
原始值到對象的轉(zhuǎn)換也非常簡單,原始值通過調(diào)用構(gòu)造函數(shù),轉(zhuǎn)化為包裝對象。
null和undefined除外,他們太特殊了,他們不會到對象進(jìn)行正常的轉(zhuǎn)化。
其他類型的原始值會按照上表的方式進(jìn)行轉(zhuǎn)換。
下面我們介紹一下,由對象轉(zhuǎn)化為原始值的過程:
JavaScript中對象到字符串的轉(zhuǎn)換經(jīng)過如下步驟:
1.如果對象具有toString(),則調(diào)用這個方法,如果該方法返回一個原始值,則最后轉(zhuǎn)換成字符串。
2.如果對象沒有toString()方法,或者這個方法并不返回一個原始值,那么JavaScript會調(diào)用valueOf()方法,如果返回的是原始值,最后就轉(zhuǎn)換為字符串。
如果JavaScript無法從toString()和valueOf()中獲得一個原始值,就會拋出類型錯誤的異常。
對象到數(shù)字的轉(zhuǎn)換過程中:JavaScript優(yōu)先調(diào)用valueof()方法,再調(diào)用toString()。
我們可以知道,利用!
,+
,==
進(jìn)行隱式類型轉(zhuǎn)換
在這里,我們有必要了解==
的類型轉(zhuǎn)換機(jī)制,如下:
1.如果兩個操作數(shù)的類型相同,則和上文所述的嚴(yán)格相等的比較規(guī)則一樣。如果嚴(yán)格相等,那么比較結(jié)果為相等。如果它們不嚴(yán)格相等,則比較結(jié)果為不相等。
2.如果兩個操作數(shù)類型不同,“==”相等操作符也可能會認(rèn)為它們相等。檢測相等將會遵守如下規(guī)則和類型轉(zhuǎn)換:
- 如果一個值是null,另一個是undefined,則它們相等。
- 如果一個值是數(shù)字,另一個是字符串,先將字符串轉(zhuǎn)換為數(shù)字,然后使用轉(zhuǎn)換后的值行比較。
- 如果其中一個值是true,則將其轉(zhuǎn)換為1再進(jìn)行比較。如果其中一個值是false,則將其轉(zhuǎn)換為0再進(jìn)行比較。
- 如果一個值是對象,另一個值是數(shù)字或字符串,則使用上面講到的規(guī)則先將對象(先調(diào)用valueof()方法,再調(diào)用toString())轉(zhuǎn)化為原始值,再進(jìn)行下一步的比較。
說完了,隱式的類型轉(zhuǎn)換,我們再看一下js語言提供的顯式類型轉(zhuǎn)換:
首先就是最簡單的Boolean()
,Number()
,String()
和Object()
,此外我們知道的toString()
方法和String()
返回的結(jié)果是一樣的。
Number('3') // => 3 String({}) // => '[object Object]' String([]) // => '' Boolean([]) // => true Boolean('0') // => true Boolean(0) // => false Object(3) // => new Number(3)
其次就是我們知道的一些全局函數(shù):toFixed
,toExponential
,toPrecision
,parseInt
,parseFloat
var n = 123.45 n.toFixed(0) // => '123' n.toFixed(2) // => '123.45' // 根據(jù)指定小數(shù)點(diǎn)后的位數(shù),返回字符串 n.toExponential(1) // => '1.2e+5' // 將數(shù)字進(jìn)行科學(xué)計(jì)數(shù)法,傳入?yún)?shù)為小數(shù)點(diǎn)后數(shù)字個數(shù),返回一個字符串 n.toPrecision(4) // => '123.4' // 傳入?yún)?shù)為保留數(shù)字的個數(shù),返回一個字符串
類型檢測
首先介紹一下typeof
typeof
運(yùn)算符返回的不是該變量的類型,而是該變量持有值的類型。在js中直接訪問一個未聲明的變量,會拋出異常,但是在typeof a
中,不會拋出異常,并且返回undefined
。這樣就能通過判斷是否存在該變量而安全使用該變量。typeof
運(yùn)算符適合于檢測原始類型和函數(shù)。
typeof undefined === 'undefined' typeof true === 'boolean' typeof 42 === 'number' typeof 'str' === 'string' typeof Symbol() === 'symbol' typeof null === 'object' typeof function () {} === 'function'
其次介紹一下instanceof
{a: 1} instanceof Object
:右操作符是一個函數(shù)構(gòu)造器,其原理是判斷左邊對象的原型鏈上是否有右邊構(gòu)造器的prototype
屬性。不同window或iframe間的對象不能使用instanceof。
[1,2] instanceof Array // => true [1,2] instanceof Object // => true '3' instanceof String // => false new String('3') instanceof String // => true new String('3') instanceof Object // => true
因此我們看出instanceof
的問題,他對于原始數(shù)據(jù)類型根本無法檢測,對引用數(shù)據(jù)類型也不能很清楚的判定類別。而且,一旦修改了原型鏈環(huán)節(jié)上的prototype
,檢測就無法使用。
然后我們再來看一下constructor
我們首先明確一下這個概念:
Object.prototype.constructor === Object // => true String.prototype.constructor === String // => true
構(gòu)造函數(shù)的prototype
中的constructor屬性指向的是這個構(gòu)造函數(shù)本身,因此我們可以利用這個特點(diǎn)。
'1'.constructor === String // => true (1).constructor === Number // => true [1,2,3].constructor === Array // => true
除了undefined
和null
,其他類型的變量均能使用constructor
判斷出類型。
但是constructor
可以被靠前的原型鏈覆蓋。
var a = [1,2,3] a.constructor = Object a.constructor === Array // => false
所以這個也不是很靠譜
最后我們來看一下Object.prototype.toString.call
這個方法百試百靈,是目前公認(rèn)的最靠譜檢測數(shù)據(jù)類型的方法
var toString = Object.prototype.toString; console.log(toString.call(new Date) === '[object Date]') //true console.log(toString.call(new String) ==='[object String]') //true console.log(toString.call(new Function) ==='[object Function]') //true console.log(toString.call(Type) ==='[object Function]') //true console.log(toString.call('str') ==='[object String]') //true console.log(toString.call(Math) === '[object Math]') //true console.log(toString.call(true) ==='[object Boolean]') //true console.log(toString.call(/^[a-zA-Z]{5,20}$/) ==='[object RegExp]') //true console.log(toString.call({name:'wenzi', age:25}) ==='[object Object]') //true console.log(toString.call([1, 2, 3, 4]) ==='[object Array]') //true console.log(toString.call(undefined) === '[object Undefined]') //true console.log(toString.call(null) === '[object Null]') //true
以上就是javascript數(shù)據(jù)類型之原始類型詳解的詳細(xì)內(nèi)容,更多關(guān)于javascript數(shù)據(jù)類型原始類型的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaScript復(fù)原何同學(xué)B站頭圖細(xì)節(jié)示例詳解
這篇文章主要為大家介紹了JavaScript復(fù)原何同學(xué)B站頭圖細(xì)節(jié)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07JS實(shí)現(xiàn)可恢復(fù)的文件上傳示例詳解
這篇文章主要為大家介紹了JS實(shí)現(xiàn)可恢復(fù)的文件上傳示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12微信小程序封裝http訪問網(wǎng)絡(luò)庫實(shí)例代碼
這篇文章主要介紹了微信小程序封裝http訪問網(wǎng)絡(luò)庫實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05Electron學(xué)習(xí)應(yīng)用程序打包實(shí)例詳解
這篇文章主要介紹了Electron學(xué)習(xí)應(yīng)用程序打包實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09前端算法題解leetcode36-有效的數(shù)獨(dú)示例
這篇文章主要為大家介紹了前端算法題解leetcode36-有效的數(shù)獨(dú)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09