「前端料包」多是最透徹的JavaScript數據類型詳解

前言

接觸寫博客有一段時間了,都是邊學邊學着寫,但總感受寫的凌亂,想起啥寫啥。這幾天在刷紅寶書,收穫仍是蠻多的,決定結合本身的學習,寫一個系列,我叫它「前端料包」,旨在鞏固前端基礎,努力提高本身,同時也樂於作一個分享者。這個系列包括但不限於下面腦圖中的內容,目錄和發文順序暫且以下:html

概述

在講數據類型以前,想先講講變量JavaScript的變量是鬆散型的,便可以保存任何類型的數據,在JavaScript中咱們使用var關鍵字來聲明一個變量(es6中新增了let 、 const 來聲明變量)前端

var message1 = 10;
var message2 = true;
var message3 = "hello world";
複製代碼

固然,由於鬆散型,也能夠改變同一變量的值的類型,雖然不推薦這麼作,可是在JavaScript中徹底有效。es6

var message1 = 10;
    message1 = true;// 從新給變量message賦不一樣類型的值,數據類型也會隨之改變,不推薦這樣作
複製代碼

而咱們全部數據類型的值都是保存在自定義的某一個變量中。 JavaScript從誕生到如今最新的正式版本,一共有7種數據類型,其中有6種基本數據類型:Undefined、Null、Boolean、Number、String和Symbol(es6新增);1種引用數據類型——ObjectObject本質上是由一組無序的名值對組成的)。JavaScript不支持任何建立自定義類型的機制,而全部值最終都將是上述 7 種數據類型之一。這裏要特別說明一下,最新的基本數據類型的第7個兄弟——BigInt已經誕生,將在下一版本(es10)中做爲新特性出現(V8引擎v6.7 默認啓用對 BigInt 的支持)。segmentfault

BigInt是什麼? BigIntJavaScript中一種能夠用來表示任意精度整數的基本數據類型數組

BigInt能夠用來表示任意精度整數的特性爲JavaScript解鎖了更多的騷操做,使用BigInt能夠告別過去由於整數運算致使溢出的痛苦。特別是金融方面由於涉及大量的數據運算,好比高精度時間戳,或者數值過大的ID,這些是沒法安全的用Number類型去存儲的,因此退而求其次使用String類型去存儲,有了BigInt類型後就能夠安全的將其存儲爲數值類型。瀏覽器

另外BigInt的實現也爲實現BigDecimal打下堅實基礎,那將對於以十進制精度表示貨幣金額並對其進行精確運算(也就是0.10 + 0.20 !== 0.30問題)很是有幫助安全

關於BigInt能夠看看這篇文章:JavaScript基本類型之--BigIntapp

一、 Undefined類型

Undefined類型只有一個值,即特殊的undefined,一個變量在聲明後未初始化時,這個變量的值就是undefined函數

var message;
alert(message);// undefined
複製代碼

須要注意的是聲明瞭但未初始化的變量與未聲明的變量是不同的性能

var msg;
alert(msg);// 聲明瞭未初始化(即賦值),默認值爲"undefined"
alert(a);// 報錯
複製代碼

但使用typeof操做符來檢測上面兩個變量時,都會返回undefined

var msg;
// var a
alert(typeof msg);// "undefined"
alert( typeof a);// "undefined"
複製代碼

這個結果有其邏輯上的合理性。由於雖然這兩種變量從技術角度看有本質區別,但實際上不管對哪一種變量也不可能執行真正的操做。

未初始化的變量會自動被賦予 undefined 值,沒有必要將變量顯式的設置爲undefined,但顯式地初始化變量依然是明智的選擇。若是可以作到這一點,那麼當 typeof 操做符返回"undefined"值時,咱們就知道被檢測的變量尚未被聲明,而不是還沒有初始化。

二、Null類型

Null 類型是第二個只有一個值的數據類型,這個特殊的值是 nullnull 值表示一個空對象指針,使用 typeof 操做符檢測 null 值時會返回"object"

var jake = null; 
alert(typeof jake ); // "object"
複製代碼

雖然 typeof null 會輸出 object,但這只是 JS 存在的一個悠久 Bug。在 JS 的最第一版本中使用的是 32 位系統,爲了性能考慮使用低位存儲變量的類型信息,000 開頭表明是對象然而 null 表示爲全零,因此將它錯誤的判斷爲 objectnull值的主要做用是若是定義的變量在未來用於保存對象,那麼最好將該變量初始化爲null值。

三、Boolean類型

Boolean類型是JavaScript中使用最多的一種基本數據類型,只有兩個值truefalse(全爲小寫)。

var  a = true;
var  b = false;
複製代碼

雖然Boolean類型只有兩個值,但JavaScript中全部類型的值都有與這兩個Boolean值等價的值,能夠調用轉型函數Boolean()將其餘類型的值轉化爲Boolean值。

var  msg = "hello world";
var  magBoolean = Boolean(msg);
複製代碼

根據轉換值的數據類型及其實際值,返回一個Boolean值。各類數據類型及其對應的轉換規則以下表:

數據類型 轉爲true 轉爲false
Boolean true false
String 任何非空字符串 " "(空字符串)
Number 任何非零數字(包括無窮大) 0和NaN
Object 任何對象 null
Undefined not applicable(不適用) undefined

四、Number類型

Number類型算是JavaScript中最複雜也最使人關注的基本數據類型了,Number能夠同時表示整數和浮點數值,同時也支持各類進制和科學計數法。具體以下

var intNum = 55; // 整數

// 浮點數
var floatNum1 = 1.1; 
var floatNum2 = 0.1; 
var floatNum3 = .1; // 有效,但不推薦

// 科學計數法
var floatNum = 3.125e7; // 等於 31250000 至關於 3.125*10的7次方

// 八進制(以O開頭),數字序列(0~7)
var octalNum1 = 070; // 八進制的 56
var octalNum2 = 079; // 無效的八進制數值——解析爲 79
var octalNum3 = 08; // 無效的八進制數值——解析爲 8

// 十六進制(以Ox開頭),數字序列(0~9及A~F),字母大小寫同等
var hexNum1 = 0xA; // 十六進制的 10
var hexNum2 = 0x1f; // 十六進制的 31

複製代碼

JavaScript可以表示的最小數值保存在 Number.MIN_VALUE 中——在大多數瀏覽器中,這個值是 5e-324;可以表示的最大數值保存在Number.MAX_VALUE 中——在大多數瀏覽器中,這個值是 1.7976931348623157e+308。若是某次計算的結果獲得了一個超出 JavaScript 數值範圍的值,那麼這個數值將被自動轉換成特殊的 Infinity 值(有正負)。 這裏要特別說明一下,浮點數值的最高精度是 17 位小數,但在進行算術計算時其精確度遠遠不如整數。例如,0.1 + 0.2的結果不是 0.3,而是 0.30000000000000004。這是由於0.1和0.2在轉換成二進制後會無限循環,因爲標準位數的限制後面多餘的位數會被截掉,此時就已經出現了精度的損失,相加後因浮點數小數位的限制而截斷的二進制數字在轉換爲十進制就會變成0000000000000004。因此上面提到的BigInt就應運而生。

關於浮點數值計算會產生舍入偏差的問題,有一點須要明確:這是使用基於IEEE754 數值的浮點計算的通病,ECMAScript 並不是獨此一家;其餘使用相同數值格式的語言也存在這個問題。

NaN

NaN(Not a Number),即非數值,用於表示一個原本要返回數值的操做數未返回數值的狀況。 NaN有兩個非同尋常的特色:

  1. 任何涉及NaN的操做(例如NaN/10)都會返回NaN
  2. NaN與任何值都不相等,包括NaN自己。

針對這兩個特色,JavaScript定義了isNaN()函數。這個函數接受一個參數,該參數能夠是任何類型。isNaN()在接收到一個值後,會嘗試將這個值轉換爲數值,而任何不能被轉換爲數值的值都會致使函數返回true

alert(isNaN(NaN)); //true 
alert(isNaN(10)); //false(10 是一個數值)
alert(isNaN("10")); //false(能夠被轉換成數值 10)
alert(isNaN("blue")); //true(不能轉換成數值)
alert(isNaN(true)); //false(能夠被轉換成數值 1)
複製代碼

數值轉換

JavaScript提供3個函數能夠把非數值轉換爲數值:

  • Number()能夠用於任何數據類型
  • parseInt()parseFloat()專門用於把字符串轉換爲數值

Number() Number()函數的轉換規則不少,這裏直接引用紅寶書裏的描述:

若是是 Boolean 值,true 和 false 將分別被轉換爲 1 和 0。

若是是數字值,只是簡單的傳入和返回。

若是是 null 值,返回 0。  若是是 undefined,返回 NaN。

若是是字符串,遵循下列規則:

a、中只包含數字(包括前面帶正號或負號的狀況),則將其轉換爲十進制數值,即"1" 會變成 1,"123"會變成 123,而"011"會變成 11(注意:前導的零被忽略了);

b、串中包含有效的浮點格式,如"1.1",則將其轉換爲對應的浮點數值(一樣,也會忽 略前導零);

c、字符串中包含有效的十六進制格式,例如"0xf",則將其轉換爲相同大小的十進制整 數值;

d、字符串是空的(不包含任何字符),則將其轉換爲 0;  若是字符串中包含除上述格式以外的字符,則將其轉換爲 NaN。

若是是對象,則調用對象的 valueOf()方法,而後依照前面的規則轉換返回的值。若是轉換 的結果是 NaN,則調用對象的 toString()方法,而後再次依照前面的規則轉換返回的字符 串值。

var num1 = Number("Hello world!"); //NaN 
var num2 = Number(""); //0 
var num3 = Number("000011"); //11 
var num4 = Number(true); //1 
NumberExample04.htm
複製代碼

parseInt() parseInt()函數在轉換字符串時,更多的是看其是否符合數值模式。它會忽略字 符串前面的空格,直至找到第一個非空格字符。若是第一個字符不是數字字符或者負號,parseInt() 就會返回 NaN;也就是說,用 parseInt()轉換空字符串會返回 NaNNumber()對空字符返回 0)。如 果第一個字符是數字字符,parseInt()會繼續解析第二個字符,直到解析完全部後續字符或者遇到了 一個非數字字符。

var num1 = parseInt("1234blue"); // 1234 
var num2 = parseInt(""); // NaN 
var num3 = parseInt("0xA"); // 10(十六進制數)
var num4 = parseInt(22.5); // 22 
var num5 = parseInt("070"); // 56(八進制數)
var num6 = parseInt("70"); // 70(十進制數)
var num7 = parseInt("0xf"); // 15(十六進制數)
複製代碼

在使用 parseInt()解析像八進制字面量的字符串時,ECMAScript 35 存在分歧。

//ECMAScript 3 認爲是 56(八進制),ECMAScript 5 認爲是 70(十進制)
var num = parseInt("070");
複製代碼

所以parseInt()引入第二個參數:轉換時使用的基數,以解決上述困惑。

var num1 = parseInt("10", 2); //2 (按二進制解析)
var num2 = parseInt("10", 8); //8 (按八進制解析)
var num3 = parseInt("10", 10); //10 (按十進制解析)
var num4 = parseInt("10", 16); //16 (按十六進制解析)
複製代碼

parseFloat()parseInt()函數相似,parseFloat()也是從第一個字符(位置 0)開始解析每一個字符。並且也是一直解析到字符串末尾,或者解析到碰見一個無效的浮點數字字符爲止。

var num1 = parseFloat("1234blue"); //1234 (整數)
var num2 = parseFloat("0xA"); //0 
var num3 = parseFloat("22.5"); //22.5 
var num4 = parseFloat("22.34.5"); //22.34 第二個小數點無效
var num5 = parseFloat("0908.5"); //908.5 
var num6 = parseFloat("3.125e7"); //31250000
複製代碼

五、String類型

String,即字符串,由一對雙引號或單引號表示(單雙引號沒有區別)

var firstName = "Jake";
var lastName = 'JakeZhang';
複製代碼

JavaScript中的字符串是不可變的,也就是說,字符串一旦建立,它們的值就不能改變。要改變某個變量保存的字符串,首先要銷燬原來的字符串,而後再用另外一個包含新值的字符串填充該變量。

var   name  = "Jake";
name = name + "Zhang";
複製代碼

實現這個操做的過程以下:首先建立一個能容納 10 個字符的新字符串,而後在這個字符串中填充"Jake"和"Zhang",最後一步是銷燬原來的字符串"Jake"和字符串"Zhang",由於這兩個字符串已經沒用了(這個過程是在後臺發生的)。

實際開發中常常爲方便存儲,常常須要將值轉換爲字符串。要把一個值轉換爲一個字符串有兩種方式:

一、toString() 除了nullundefined值沒有tostring()方法,其餘值都有這個方法,該方法返回字符串的一個副本。

var age = 11; 
var ageAsString = age.toString(); // 字符串"11" 
var found = true; 
var foundAsString = found.toString(); // 字符串"true"
複製代碼

toString()能夠傳入一個參數:輸出數值的基數。能夠輸出以二進制、八進制、十六進制,乃至其餘任意有效進制格式表示的字符串值。下面給出幾個例子:

var num = 10; 
alert(num.toString()); // "10" 
alert(num.toString(2)); // "1010" 
alert(num.toString(8)); // "12" 
alert(num.toString(10)); // "10" 
alert(num.toString(16)); // "a"
複製代碼

二、使用+" " 便可以經過要轉換的值 + 空字符串(" "),也能夠實現轉換。

var num = 10;
var numAsString = num + " ";// "10"
var boolean = true;
var booleanAsString = boolean + " ";// "true"

var a ;
var b = a + " ";// "undefined "

var c = null;
var d = c + " ";// "null "

var o = {
   valueOf: function() {
           return -1;
                }
} 
 var m = o + " ";// "-1 "

複製代碼

六、Symbol

Symboles6新增的一種原始數據類型,表示獨一無二的值,是一種惟一標識符。Symbol值經過Symbol()函數生成。

let id = Symbol("jake");
複製代碼

Symbol的主要特色是如上所說的惟一性,可用做對象的惟一屬性名,即便是用同一個變量生成的值也不相等。

let id1 = Symbol('jake');
 let id2 = Symbol('jake');
 console.log(id1 == id2);  //false
複製代碼

但咱們不排除但願可以屢次使用同一個symbol值的狀況。官方提供的Symbol.for()方法能夠作到這一點。它接受一個字符串做爲參數,而後搜索有沒有以該參數做爲名稱的 Symbol 值。若是有,就返回這個 Symbol 值,不然就新建一個以該字符串爲名稱的 Symbol 值,並將其註冊到全局。

let name1 = Symbol.for('name'); //檢測到未建立後新建
 let name2 = Symbol.for('name'); //檢測到已建立後返回
 console.log(name1 === name2); // true

複製代碼

Symbol的另外一特色是隱藏性,Symbol 做爲屬性名,遍歷對象的時候,該屬性不會出如今for...in、for...of循環中,也不會被Object.keys()Object.getOwnPropertyNames()JSON.stringify()返回。

let id = Symbol("id");
 let obj = {
  [id]:'symbol'
 };
 for(let option in obj){
     console.log(obj[option]); //空
 }

複製代碼

可是也有可以訪問的方法:Object.getOwnPropertySymbols 該方法會返回一個數組,成員是當前對象的全部用做屬性名的Symbol值。

let id = Symbol("id");
 let obj = {
  [id]:'symbol'
 };
let array = Object.getOwnPropertySymbols(obj);
 console.log(array); //[Symbol(id)]
 console.log(obj[array[0]]);  //'symbol'

複製代碼

關於Symbol這個新的數據類型更多的知識點能夠參考阮神的文章—>傳送門

七、引用數據類型——Object類型

Object類型是JavaScript中最龐大而複雜的引用數據類型,本文只作簡單介紹,後續的文章會作Object的詳細介紹。 Object,即對象,是一組數據和功能的集合。對象能夠經過執行new操做符後跟要建立 的對象類型的名稱來建立。而建立 Object 類型的實例併爲其添加屬性和(或)方法,就能夠建立自定 義對象。

var person1 = new Object(); 
person.name = "Jake"; 
person.age = 23;

var person2 = {}; //與 new Object()相同
person2.name = "Jake"; 
perso2.age = 23;

//字面量的建立方式
var person3 = {
	name:"jakezhang",//name若是是保留字、有鏈接符/空格,則要'name-p',即便用字符串
	age:23,
	action:function(){
			console.log(this.name);
			}
					
};
複製代碼

以上任意一種方式均可以建立一個對象實例,固然更多的寫法和設計思想之後的文章再作體現。在實際的開發中咱們用的最多的是字面量的方式,由於這種語法要求的代碼量少,並且可以給人封裝數據的感受。這裏引用一個紅寶書的例子:

function displayInfo(args) { 
 var output = ""; 
 if (typeof args.name == "string"){
 output += "Name: " + args.name + "\n"; 
 } 
 if (typeof args.age == "number") { 
 output += "Age: " + args.age + "\n"; 
 } 
 alert(output); 
} 
displayInfo({ 
 name: "Nicholas", 
 age: 29 
}); 
displayInfo({ 
 name: "Greg" 
});
複製代碼

代碼相信都看得懂~

Object 的每一個實例都具備下列屬性和方法:

  • constructor:保存着用於建立當前對象的函數。對於前面的例子而言,構造函數(constructor)就是 Object()

  • hasOwnProperty(propertyName):用於檢查給定的屬性在當前對象實例中(而不是在實例的原型中)是否存在。其中,做爲參數的屬性名(propertyName)必須以字符串形式指定(例如:o.hasOwnProperty("name"))。

  • isPrototypeOf(object):用於檢查傳入的對象是不是傳入對象的原型。

  • propertyIsEnumerable(propertyName):用於檢查給定的屬性是否可以使用 for-in 語句來枚舉。與hasOwnProperty()方法同樣,做爲參數的屬性名必須以字符串形式指定。

  • toLocaleString():返回對象的字符串表示,該字符串與執行環境的地區對應。

  • toString():返回對象的字符串表示。

  • valueOf():返回對象的字符串、數值或布爾值表示。一般與 toString()方法的返回值相同。

因爲在 ECMAScript 中 Object 是全部對象的基礎,所以全部對象都具備這些基本的屬性和方法。

八、基本數據類型和引用類型值的比較

基本數據類型

  1. 值是不可變的
var name = "Jake Zhang";
name.toUpperCase();//輸出 JAKE ZHANG
console.log(name);// 輸出 Jake Zhang
複製代碼

由以上代碼可看出基本數據類型的值是不可變的。

  1. 存放在棧區 基本類型值指的是簡單的數據段,按值訪問,可操做保存在變量中的實際的值,其佔據空間小、大小固定,屬於被頻繁使用的數據,因此放入棧(stack)中存儲。
  2. 值的比較
var n = 1;
var m = true;
console.log(n == m);//true
console.log(n === m);// false
複製代碼

"==":只進行值的比較,會進行數據類型轉換;

"===":不會轉換數據類型。

引用數據類型

  1. 值是可變的
var person = {
  	  name:'jake',
        age:22,
        action:function () {
         console.log("do something!")
        }
      }
person.age = 23;
console.log(person.age)// 23
複製代碼

有上面的代碼可看出引用數據 類型能夠擁有一個或多個屬性和方法,並且是能夠動態修改的。

  1. 同時存放在棧內存和堆內存 引用數據類型是存放在堆(heap)中的對象,佔據空間大、大小不固定,若是存放在棧中,會影響程序運行的性能;引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址,當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中得到實體。(有關js中棧和堆的詳解可參考文章[(www.cnblogs.com/heioray/p/9…))
  2. 值的比較 當從一個變量向另外一個變量賦引用類型的值時,一樣也會將存儲在變量中的對象的值複製一份到位新變量分配的空間中。
var person1 = {
			  age:20
      }
      var person2 = person1;
			person2.age = 23;
			console.log(person1.age = person2.age)// true
複製代碼

前面講到基本數據類型和引用數據類型存儲於內存中的位置不同,引用數據類型存儲在堆中的對象,與此同時,在棧中存儲了指針,而這個指針的指向正是堆中實體的起始位置。變量person1初始化時,person1指針指向該對象{age:20}的地址,將person1賦給person2後,person2又指向該對象{age:20}的地址,這兩個變量指向了同一個對象。所以改變其中任何一個變量,都會相互影響。

ZJK
此時,若是取消某一個變量對於原對象的引用,不會影響到另外一個變量。

var a = {age:22}
			var b = a;
			a = 1;
			console.log(b);//{age:22}
複製代碼

上面代碼中,a和b指向同一個對象,而後a的值變爲1,這時不會對b產生影響,b仍是指向原來的那個對象。

後話

小生乃前端小白一枚,寫文章的最初衷是做學習筆記,爲了讓本身對該知識點有更深入的印象和理解,寫的東西也很小白,文中若有不對,歡迎指正~ 而後就是但願看完的朋友點個喜歡,也能夠關注一波~ 我會持續輸出!

我的博客連接

CSDN我的主頁

掘金我的主頁

簡書我的主頁

相關文章
相關標籤/搜索