JS 中類型和類型轉換


  類型  

  Js中的類型是針對值來說,而不是針對變量,值就是所謂的42, 'abc', false 等能用js 操作的數據。在js 中說某某某是什么數據類型,其實是說的這些值是什么類型。值呢?有7種數據類型: number, string, boolean, null, undefined, object, symbol. 42 就是number 類型了。怎么判斷出來的,最簡單的辦法就是typeof 操作符了,它返回一個字符串來表示類型

console.log(typeof 42); // 'number'

  這時,你可能想到,不對啊,以前也用typeof 操作過變量啊?也返回類型了啊。

let a = 42
console.log(typeof a); // 'number'

  其實這里仍然是對值進行的類型判斷,因為你不能說變量a就是number 類型,因為 js 是弱類型語言,完全可以把重新賦值為一個變量。

let a = 42
console.log(typeof a); // 'number'
a = 'str';
console.log(typeof a); // 'string'

  這時,你就說不出變量a 是什么類型了。在js 中,如果對一個變量進行typeof 操作,一定是對當時變量所指向或所包含的值進行typeof 操作,變量沒有類型,值有。

  但使用typeof 進行類型判斷的時候,它有一個bug, typeof null 返回的是'object' 而不是'null'.  null 值是'null' 類型的唯一值,它竟然返回了'object'. 這時,如果你就想用typeof 操作符來判斷一個值是不是null, 你就要再加一個判斷了。只有對象和null 執行typeof 操作時候,才返回'object', 只要找出二者的不同就可以了,很簡單,所有對象都是真值,null 是假值,!a 是true 就可以區分了。所以使用typeof 判斷null的時候,如下

let a = null;
(!a && typeof a === 'object') // true

  其時 typeof 還一個小問題,就是typeof 一個函數的時候,返回的是一個'funciton', 

let a = () => {};
console.log(typeof a); // 'function'

  這可能讓人產生誤會,誤以為'funciton' 也是一種數據類型,其實並不是,所有的函數都是對象,不過這也提供了一個判斷函數的簡便方法。

  當然,typeof 操作符還有一個好處,就是如果一個變量沒有聲明的話,執行typeof 操作,不會報錯,而是返回undefined, 這就是提供了一個安全的操作,因為,如果你直接引用一個沒有聲明的變量就會報錯。有時,你想判斷一個變量存不存在,如要寫a == undefined, 完了,報錯了,引用了一個沒有聲明的變量a. 如果使用 typeof a == undefined, 沒有問題。

  Number類型

  JS 依據IEEE 754標准,使用64-bit的浮點數來表示數字。盡管沒有整數類型,但它也能准確地表示-2^53 ~2^53-1個整數,整數就是沒有小數,或小數后面全是0,如42 或42.00。如果使用整數超過這個范圍,可能要損失精度,就是計算不准確。 默認情況下, 數值的輸出方式,都是10進制,並且會把末尾的0 去掉。

var a = 42.300;
var b = 42.0;
console.log(a); // '42.3'
console.log(b) // 42

  但是如果一個數特別大或特別小的話,它會以指數形式進行輸出,在chrome 瀏覽器中,只要整數大於20次方,或小數后面有7位,就會轉化為指數

var a = 5000000000000000000000;
var b = 0.0000005;
console.log(a); // 5e+21
console.log(b) // 5e-7

  JS做算數運算時, 上溢 , 下溢和除0 並不會報錯。如果上溢,就是計算結果超出了JS能表示的最大值,它會返回Infinity, -Infinity; 如果下溢,就是計算結果無限接近0 ,超出了JS 能表示的最小值,它會返回0 或-0. 除0,簡單返回Infinity, -Infinity, 當然0/0沒有意義,返回NaN

 

Number.MIN_VALUE/2 // 0
Number.MAX_VALUE * 2 // Infinity
1/0 // Infinity
0/0  // NaN

  現實中的實數是無限的,而JS只能表示有限個實數,由於內存等原因。也就是說,當我們使用JS中的實數時,這些數只是現實中實數的近似值。IEEE-754 浮點數使用的是二進制表示法,它能准確地表示1/2, 1/8 等,但不能准確表示1/10, 也就是0.1。數值比較時就會出問題,0.1 + 0.2 === 0.3 為false. 有沒有辦法使0.1 + 0.2 和0. 3 比較時,顯示相等呢?一種是辦法是設置一個容錯的邊界, 當小於這個值 的時候,就表示這兩個值相等了,這個值在js 中是2的-52 次方Math.pow(2,-52),Es6 專門定義了這個常量Number.EPSILON,  只要兩者進行比較小於這個數,就證明這兩個值相等。

// 無限接近相等
function isCloseEnoughToEqual(n1,n2) {
    return Math.abs( n1 - n2 ) < Number.EPSILON;
}
let a = 0.1 + 0.2;
let b = 0.3 
console.log(isCloseEnoughToEqual(a, b)) // true

  計算整數沒有問題,這個整數也是有邊界的, 對於double雙精度浮點數,用 1 位表示符號,用 11 位表示指數,52 位表示尾數,所以浮點數表示的最大整數是2的53次方-1(Math.pow(2, 53))最小整數是-2的53次方, 即[-2^53, 2^53 - 1], 超出了這個邊界,計算也是不准確

let a = 9007199254740992; // 2^53
let b = a + 1;
console.log(a == b) // true

  a + 1 和a 是相等的,所以出現了問題了。為些,Es6 定義了安全數值 的字面量和判斷安全數值的方法 Number.MAX_SAFE_INTEGER, Number.MIN_SFTE_INTEGER 

以及Number.isSafeInteger(); 當然也提供了判斷整數的方法Number.isInteger(); 

let a = 9007199254740991; // 2^53 -1
let b = a + 1;
console.log(Number.isSafeInteger(a)) // true
console.log(Number.isSafeInteger(b)) // false
console.log(Number.isInteger(a)) // true
console.log(Number.isInteger(b)) // true

  a 和 b 都是整數,但a 是安全整數,b 不是

  Number 類型中的特殊值--- NaN。當我們執行算術操作的時候,這個操作並沒有成功,按理說,它應該報錯,但是Js 並不會,而是返回特殊的值NaN。

let a = 2 / 'foo';
console.log(a ) // NaN

  我們怎么判斷值是NaN, 可能最簡單的就是 它=== NaN, 但是很不幸,NaN 和它本身並不相等,NaN === NaN 返回的是false. 其次還有一個isNaN 的函數來判斷NaN, 但是它也有點問題,傳給isNaN的參數,首先會進行自動類型轉化,看能不能轉化成數字,如能就是返回true, 如果不能返回false.

let a = 2 / 'foo';
console.log(isNaN(a)) // true
let b = 'foo';
console.log(isNaN(b)) // true

  可以看到 b 不是NaN, 但是isNaN 仍然返回了true, 也就是說,即使isNaN() 返回了true, 我們也不能確定這個值是不是NaN。為了解決這個問題,ES6增加了Number.isNaN(), 如果值為NaN, 它返回ture ,否則返回false.

let a = 2 / 'foo';
console.log(Number.isNaN(a)) // true
let b = 'foo';
console.log(Number.isNaN(b)) // false

  Number.isNaN() 是怎么判斷的呢?首先它判斷這個值是不是number 類型,NaN 作為值,它其實是number 類型

let a = 2 / 'foo';
console.log(typeof a === 'number') // true

  其次才是調用isNaN 方法來判斷。

   Number 類型中的特殊值--- +0 和-0.  Js 中的0 有兩個,+0 和-0. 除了字面量的方式可以得到-0 外,使用* 或 / 操作符,也可以得到,但+ - 操作符不能。

let a = -1 * 0;
let b = 0 / -1;
console.log(a, b) // -0

  有兩個0 也不要緊,但是它有幾個奇怪的行為

    1, +0 和 -0 使用== 或=== 比較的時候,是相等。

let a = -0;
let b = 0 
console.log(a === b) // true

  其實它兩個是不相等,所以ES6 增加了Object.is() 方法,來判斷它兩個並不相等。

  2, -0 轉化成字符串的時候,輸出的結果是‘0’ ,而是不-0. JSON.stringify(-0) 也是得到'0'

let a = -0;
console.log(a.toString()) // '0'
console.log(JSON.stringify(a)) // '0'

   然而當一個'-0' 字符串轉化數字的時候,它返回的是-0, 正常的。

+"-0"; // -0
Number( "-0" ); // -0
JSON.parse( "-0" ); // -0

  null 和undefined

  null 是一個關鍵字,不能用它作為變量名,更不能給它賦值,但undefined 不是,它可以是一個標示符,可以給它賦值,當然,這僅限在非嚴格模式,如果是嚴格模式的話,會報錯。

 var undefined = 2;
 "use strict";
var undefined = 2; // 報錯

  由於undefined 可以被改寫,有一個void 操作符,它后面可以是任何表達式,返回的值是undefined, 不過一般使用 void 0, 這樣undefined 就不會被改寫了。

console.log(void 0 === undefined) // true

   對象(引用類型)

  如果一個值執行typeof 操作返回 ‘object’,那么它就是引用類型,其實,還可以進行細分,它具體是對象下面的哪一個類型,數組,還是函數,使用的是Object.prototype.toString.call(值)

let a = [1,2];
let b = function(){};
let c = new Date();

console.log(Object.prototype.toString.call(a)) // '[object Array]' Array 類型
console.log(Object.prototype.toString.call(b)) // '[object Function]' Function 類型
console.log(Object.prototype.toString.call(c)) // '[object Date]' Date 類型

  那對基本數據類型調用toString() 方法呢?它返回基本類型的包裝類型,甚至對null, undefined 都可以調用, 返回值如下

console.log(Object.prototype.toString.call(1)) // '[object Number]'
console.log(Object.prototype.toString.call('abc')) // '[object String]'
console.log(Object.prototype.toString.call(false)) // '[object Boolean]' 
console.log(Object.prototype.toString.call(null)) // '[object Null]' 
console.log(Object.prototype.toString.call(undefined)) // '[object Undefined]' 
console.log(Object.prototype.toString.call(Symbol())) // '[object Symbol]' 

  數組類型

  1, 刪除數組元素的時候,不要用delete, 使用delete 可以把數組中的元素及元素在數組中的位置(slot)刪除掉, 但不用更新數組的length 長度。

let a = [1, 2];
delete a[0];
console.log(a);
console.log(a.length)

  下圖是chrome 瀏覽器的返回值。

  可以使用splice() 進行刪除

  2, 不要創建稀疏數組。當創建稀疏數組的時候,兩個不相鄰元素之間的slot 是不存在的,a[0] 和a[2] 之間並沒有slot, 盡管獲取到它的值是undefined

var a = [ ];
a[0] = 1;
// 沒有 a[1] slot
a[2] = 3;
a[1]; // undefined
a.length; // 3

  當我們使用構造函數創建數組時, 給它傳遞一個數字,它就會創建length 為參數的數組,這個數組其實也是一個稀疏數組. 比如,你傳遞了一個3,那么創建的數組的length 為3,我們以為這個數組有了3個slot, 值為undefined, 其實它沒有任何的slot. 當你使用forEach 的時候,什么都沒有輸出

arr.forEach((item, index) => {
    console.log(item);
    console.log(index);
})

  3, 盡管數組是對象,但千萬不要作為對象進行使用,比如 a["name"] = 'Sam', 尤其是屬性是數字字符串的時候,這時它會把字符串轉化為數字,作為數組的元素

let a = [];
a['5'] = 5;
console.log(a.length) // 6

   類型轉化

  首先要記住的是,Js中的類型轉化,它得到的結果永遠是基本類型,也就可以分為兩種情況,基本類型和基本類型之間的轉化,引用類型轉化為基本類型,不可能出現基本類型轉化為引用類型。類型轉化出現最多就是轉化成number, string, 和boolean. 這就是說,我們在學習類型轉化的時候,也要分為兩種情況去學,比如轉化成string,  基本類型是怎么轉化string的,引用類型是怎么轉化成string的。

  

  

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM