【基礎系列】javascript數據類型(原始類型)

開闢了一個關於javascript的基礎系列,更加深刻、細緻的瞭解這門語言。今天分享的是js的數據類型。

javascript的數據類型能夠分爲兩類:原始類型(基礎數據類型)對象類型(引用數據類型)
原始類型包括:數字字符串布爾值、以及特殊的undefinednull
除了以上的數據類型,其餘就都是對象類型了
具備表明性的對象類型有:對象(object)數組(array)函數(function)javascript

本次咱們着重介紹原始數據類型java

兩個小注意點:
1.js語言是弱類型語言(並非沒有數據類型)
2.在js語言中所聲明的變量是沒有數據類型的,所以能夠被賦予任何類型的值es6

原始類型(基礎數據類型)

數字

和其餘變成語言不一樣,js不區分正整數值和浮點數值,js中全部數字都是用浮點數值表示的。數組

數字的算術運算符方法有+-*/%(加,減,乘,除,餘)。
除此以外,js還支持更復雜的算術運算,這些複雜運算經過做爲Math對象的屬性定義和常量來實現:安全

// 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的隨機數
Math.random()
// 圓周率
Math.PI
// e天然對數的底數
Math.E
// 3的開平方根
Math.sqrt(3)
// 3的開立方根
Math.pow(3, 1/3)
// 三角函數
Math.sin(0)
// 求10的天然對數
Math.log(10)
// 以10爲底數的100的對數
Math.log(100)/Math.LN10
// 以2爲底數的512的對數
Math.log(512)/Math.LN2
// e的3次方冪
Math.exp(3)

js的數字表示範圍是有限制的(可否表示的限制可否知足精度到個位的限制以及可否做爲數組索引的限制
具體的狀況以下圖:
Number數軸
(圖片來自網絡,侵刪)網絡

所以javascript在進行數學運算時,會出現溢出下溢兩種狀況,
溢出的狀況爲:
當運算結果超出了js語言所能表示的上線(即圖中1.8e308正無窮的區域),結果會返回Infinity(表示無窮大)
一樣的,當計算的負數的值超過了能表示的負數範圍(即圖中-1.8e308負無窮的區域),結果會返回-Infinity(表示負無窮大)dom

下溢的狀況爲:
當運算的結果無限接近於0,並比js能表示的最小值還小的狀況(即圖中05e-324的區域)。這樣結果會返回0
一樣的,當一個負數發生下溢(即圖中0-5e-324的區域),這時結果會返回一個-0函數

上文,咱們介紹數字中預約義的全局變量Infinity,此外還有一個預約義的全局變量NaN(表示非數字,not-a-number,當運算的結果並非一個數字值的時候,會返回NaN測試

在js中,NaN有特殊的一點,就是它和任何值都不相等(包括自身),所以想要判斷一個值是否爲NaN,可使用x != x判斷編碼

var x = 1 - 'a'
x != x //true

除此以外,咱們還能夠調用全局預約好的函數isNaN

// 當傳入的參數只要不是一個數字,就返回true
isNaN(5 - 'a') // true
isNaN('1') // true
isNaN('a') // true
isNaN({a: 2}) // true
isNaN(1) //false
isNaN(Infinity) //false

另外,全局還有一個預約好的函數isFinite

// 當傳入的參數只要不是NaN, Infinity, -Infinity就返回true
isFinite(5 - 'a')  // false
isFinite('1')  // false
isFinite('a')  // false
isFinite({a: 2})  // false
isFinite(1)  // true
isFinite(Infinity) //false

數學中實數有無限多個,而在javascript語言中能經過浮點數的形式只能表現其中的有限個,所以在js中使用實數的時候,咱們每每都是使用的一個近似值。
javscript所採用的浮點數表示發,是一種二進制表示法,所以咱們能夠精確的表示1/21/81/1024。可是在數學中,咱們經常使用的都是十進制分數1/10。因此js中並不能精確的表示像0.1這樣簡單的數字。

var x = 0.3-0.2
var y = 0.2-0.1
x == y // false

所以要避免在js中用浮點數進行計算(儘可能使用整數

文本

javascript中的字符串採用的是UTF-16編碼的Unicode字符集,字符串的長度是其含有16位值的個數,以下:

var a = 'z'
var b = '?' // 注意,這個字不是「吉祥」的吉
a.length // => 1: a包含的一個16位值 \u007A
b.length // => 2: b包含兩個16位值 \uD842\uDFB7

在js語言中,字符串是由單引號或雙引號括起來的字符序列,定義的由單引號定界的字符串中能夠包含雙引號,一樣,定義的由雙引號定界的字符串中也能夠包含單引號。

字符串能夠拆分爲數行,每行必須以\結束,若是但願在字符串中再起一行可使用轉義字符\n

所有的轉義字符以下:

clipboard.png

測試輸出結果以下:

clipboard.png

可是,在ES6中,新增了模板字符串,模板字符串是用反勾號`將字符括起
在模板字符串中換行就簡單不少:

`
hello
world
`
// 等價於
'hello\nworld'

除此以外,模板字符串還支持元素注入

var str = 'world'
`hello ${world}`
// 等價於
'hello ' + str

除了字符串的length屬性以外,字符串還有不少能夠調用的方法

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, 返回首次出現l的位置
str.lastIndexOf('l')    // 10,返回最後一次出現l的位置
str.split(", ")    // ['Hello', 'World'], 分割爲數組
str.replace('H', 'h')    // 'hello, World', 將h替換爲H
str.toUpperCase()    // 'HELLLO, WORLD', 將字符串全部字母變爲大寫
str.toLowerCase()    // 'hello, world', 將字符串全部字母變爲小寫

// es6新增方法
let s = 'Hello world!';

s.startsWith('Hello') // true
s.endsWith('!') // true
s.includes('o') // true

// includes():返回布爾值,表示是否找到了參數字符串。
// startsWith():返回布爾值,表示參數字符串是否在原字符串的頭部。
// endsWith():返回布爾值,表示參數字符串是否在原字符串的尾部。

'x'.repeat(3) // "xxx"
'hello'.repeat(2) // "hellohello"
'na'.repeat(0) // ""

// repeat方法返回一個新字符串,表示將原字符串重複n次

須要注意的是,對於字符串的任何方法都會返回一個新的字符串,而不會在原字符串上修改。

布爾值

javascript中布爾值有兩個truefalse

null&undefined

null和undefined都表示」值的空缺「,但事從背後更深遠的角度考慮,他們的仍是有差異的。
對null進行typeof檢測,返回值是object
對undefined進行typeof檢測,返回值是undefined
undefined表示,對這個值還未定義,尚未進行初始化。好比,當咱們聲明一個變量,可是卻未賦值,此時會返回undefined,當咱們獲取一個對象未定義的屬性,此時會返回undefined,當咱們調用一個函數,卻未傳參,參數會返回undefined。
null表示,沒有對象,此處沒有值,此處不該該有值。好比,原型鏈的重點就是null。

後面會從的角度進行另外一番解釋。

咱們能夠這麼理解,undefined是系統級的、出乎意料的、相似錯誤的空缺。而null是程序級的、正常的、在乎料之中的值的空缺。在某些場景下,好比想賦值給一個變量,想表示變量爲空,或做爲參數傳入一個函數,這是,最佳的選擇是null。

包裝對象

瞭解包裝對象以前,咱們首先思考這麼一個問題。

var a = 'test'
a.length    //4

咱們知道上面代碼中的a是一個字符串,字符串不是一個對象,不能進行.關鍵字的操做。可是,爲何咱們能夠獲得a.length呢?

由於只要存在包裝對象的概念,在上述代碼執行的過程當中,js會將字符串經過new String的方式生成一個包裝對象,這個對象繼承了String的方法,由於能夠經過.的方式訪問到。一旦屬性的引用結束,這個包裝對象就會被銷燬(其實在js語言內部的實現上不必定建立或銷燬這個對象,可是整個過程在執行層面看起來是這樣的,咱們也能夠這麼進行理解)

原始類型和引用類型的變與不變關係

想要深刻理解原始類型和引用類型的變與不變,相等比較等問題的時候,咱們須要藉助的思想來理解,咱們能夠這麼思考:

clipboard.png
(圖片來自網絡,侵刪)

這張圖闡述了原始類型和引用類型的關係:
原始類型保存在棧內存中,原始類型(包括字符串、數字、布爾型、undefined)是保存在棧內存中,是不能夠修改的(咱們所看到的修改,其實都是刪除後從新賦值),當複製一個原始類型的時候,其實就是在內存中複製這麼值。其中,undefined表明的就是未被賦值的一個棧內存的區域。

引用類型保存在堆內存中,可是在棧內存中存了一個引用類型的地址,棧內存中的地址有一個指針指向堆內存的引用類型。這個引用類型是能夠進行修改的,好比咱們能夠向數組中push一個新值。若是咱們只是簡單的複製一個引用類型(淺拷貝),那麼其實複製的是這個在棧內存中的地址,複製後的值發生修改,那麼以前被複制的值也一樣會被修改,所以在複製引用類型的時候,最好要進行深拷貝。其中,null很特殊,表示的是在棧內存中,有一個指針指向堆內存中的引用類型,一旦這個指針掉了,就是null。

類型轉換

jacascript中的類型轉換很是常見,也是javascript語言中很是重要的一點。
首先咱們來看一下類型轉化表:

clipboard.png

任意JavaScript的值均可以轉換爲布爾值,只有undefine、null、0、NaN、""會被轉換爲false,其餘全部值都會被轉換成true。

當字符串轉化爲數字數字時,那些數字表示的字符串能夠轉化爲數字,也容許在開始和結尾處有空格,可是其餘含有非空非數字字符都不會完成到數字的轉化,他們會轉化爲NaN

原始值到對象的轉換也很是簡單,原始值經過調用構造函數,轉化爲包裝對象。

null和undefined除外,他們太特殊了,他們不會到對象進行正常的轉化。

其餘類型的原始值會按照上表的方式進行轉換。

下面咱們介紹一下,由對象轉化爲原始值的過程:

JavaScript中對象到字符串的轉換通過以下步驟:
1.若是對象具備toString(),則調用這個方法,若是該方法返回一個原始值,則最後轉換成字符串。
2.若是對象沒有toString()方法,或者這個方法並不返回一個原始值,那麼JavaScript會調用valueOf()方法,若是返回的是原始值,最後就轉換爲字符串。

若是JavaScript沒法從toString()和valueOf()中得到一個原始值,就會拋出類型錯誤的異常。

對象到數字的轉換過程當中:
JavaScript優先調用valueof()方法,再調用toString()。

咱們能夠知道,利用!+==進行隱式類型轉換
在這裏,咱們有必要了解==的類型轉換機制,以下:

1.若是兩個操做數的類型相同,則和上文所述的嚴格相等的比較規則同樣。若是嚴格相等,那麼比較結果爲相等。若是它們不嚴格相等,則比較結果爲不相等。

2.若是兩個操做數類型不一樣,「==」相等操做符也可能會認爲它們相等。檢測相等將會遵照以下規則和類型轉換:

  • 若是一個值是null,另外一個是undefined,則它們相等。
  • 若是一個值是數字,另外一個是字符串,先將字符串轉換爲數字,而後使用轉換後的值行比較。
  • 若是其中一個值是true,則將其轉換爲1再進行比較。若是其中一個值是false,則將其轉換爲0再進行比較。
  • 若是一個值是對象,另外一個值是數字或字符串,則使用上面講到的規則先將對象(先調用valueof()方法,再調用toString())轉化爲原始值,再進行下一步的比較。

說完了,隱式的類型轉換,咱們再看一下js語言提供的顯式類型轉換
首先就是最簡單的Boolean()Number()String()Object(),此外咱們知道的toString()方法和String()返回的結果是同樣的。

Number('3')    // => 3
String({})    // => '[object Object]'
String([])    // => ''
Boolean([])    // => true
Boolean('0')    // => true
Boolean(0)    // => false
Object(3)    // => new Number(3)

其次就是咱們知道的一些全局函數:toFixedtoExponentialtoPrecisionparseIntparseFloat

var n = 123.45
n.toFixed(0)    // => '123'
n.toFixed(2)    // => '123.45'
// 根據指定小數點後的位數,返回字符串
n.toExponential(1) // => '1.2e+5'
// 將數字進行科學計數法,傳入參數爲小數點後數字個數,返回一個字符串
n.toPrecision(4)  // => '123.4'
// 傳入參數爲保留數字的個數,返回一個字符串

類型檢測

首先介紹一下typeof

typeof運算符返回的不是該變量的類型,而是該變量持有值的類型。在js中直接訪問一個未聲明的變量,會拋出異常,可是在typeof a中,不會拋出異常,而且返回undefined。這樣就能經過判斷是否存在該變量而安全使用該變量。typeof運算符適合於檢測原始類型和函數。

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:右操做符是一個函數構造器,其原理是判斷左邊對象的原型鏈上是否有右邊構造器的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的問題,他對於原始數據類型根本沒法檢測,對引用數據類型也不能很清楚的斷定類別。並且,一旦修改了原型鏈環節上的prototype,檢測就沒法使用。

而後咱們再來看一下constructor

咱們首先明確一下這個概念:

Object.prototype.constructor === Object    // => true
String.prototype.constructor === String    // => true

構造函數的prototype中的constructor屬性指向的是這個構造函數自己,所以咱們能夠利用這個特色。

'1'.constructor === String    // => true
(1).constructor === Number    // => true
[1,2,3].constructor === Array    // => true

除了undefinednull,其餘類型的變量均能使用constructor判斷出類型。
可是constructor能夠被靠前的原型鏈覆蓋。

var a = [1,2,3]
a.constructor = Object
a.constructor === Array    // => false

因此這個也不是很靠譜

最後咱們來看一下Object.prototype.toString.call
這個方法百試百靈,是目前公認的最靠譜檢測數據類型的方法

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

建議使用這個方法!

最後,最近一段時間個人博客會保持長時間更新,針對文章有什麼問題,你們能夠在下方留言,感謝!
相關文章
相關標籤/搜索