JavaScript: 自動類型轉換

咱們都知道,JavaScript是類型鬆散型語言,在聲明一個變量時,咱們是沒法明確聲明其類型的,變量的類型是根據其實際值來決定的,並且在運行期間,咱們能夠隨時改變這個變量的值和類型,另外,變量在運行期間參與運算時,在不一樣的運算環境中,也會進行相應的自動類型轉換。正則表達式

自動類型轉換通常是根運行環境操做符聯繫在一塊兒的,是一種隱式轉換,看似難以捉摸,實際上是有必定規律性的,大致能夠劃分爲:轉換爲字符串類型轉換爲布爾類型轉換爲數字類型。今天咱們就介紹一下這幾種轉換機制。算法

1. 轉換爲字符串類型(to string)

加號「+」做爲二元操做符(binary)而且其中一個操做數爲字符串類型時,另外一個操做數將會被無條件轉爲字符串類型:編程

// 基礎類型

var foo = 3 + '';            // "3"

var foo = true + '';         // "true"

var foo = undefined + '';    // "undefined"

var foo = null + '';         // "null"

// 複合類型 var foo = [1, 2, 3] + ''; // "1,2,3" var foo = {} + ''; // "[object Object]" // 重寫valueOf()和toString() var o = { valueOf: function() { return 3; }, toString: function() { return 5; } }; foo = o + ''; // "3" o = { toString: function() { return 5; } }; foo = o + ''; // "5"

從上面代碼中能夠看到,對於基礎類型,會直接轉爲與字面量相一致的字符串類型,而對於複合類型,會先試圖調用對象的valueOf()方法,若是此方法返回值是引用類型,則接着再調用其toString()方法,最後將返回值轉爲字符串類型。上面咱們定義了一個對象,包含valueOf()和toString()方法,而後和一個空字符串進行運算,能夠看得出來,它是調用了valueOf()方法,而後咱們重寫此對象,將valueOf()移除,也就是不重寫object的valueOf()方法,從最後的結果來看,它最終是調用了toString()方法,而後將返回的數字類型5與空字符串進行運算,最終獲得一個字符串類型的值。數組

2. 轉爲布爾類型(to boolean)

a. 數字轉爲布爾類型(from number)

當數字在邏輯環境中執行時,會自動轉爲布爾類型。0和NaN會自動轉爲false,其他數字都被認爲是true,代碼以下:編程語言

// 0和NaN爲false,其他均爲true

if (0) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

if (-0) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

if (NaN) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

// 其他數字均爲true

if (-3) {
    console.log('true');    // output: true
} else {
    console.log('false');
}


if (3) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

從上面的代碼中能夠看出,非0負值也會被認爲是true,這一點須要注意。函數

b. 字符串轉爲布爾類型(from string)

和數字相似,當字符串在邏輯環境中執行時,也會被轉爲布爾類型。空字符串會被轉爲false,其它字符串都會轉爲true,代碼以下:spa

// 空字符串爲false

if ('') {
    console.log('true');
} else {
    console.log('false');    // output: false
}

// 其餘字符串均爲true

if ('0') {
    console.log('true');    // output: true
} else {
    console.log('false');
}

if ('false') {
    console.log('true');    // output: true
} else {
    console.log('false');
}

c. undefined和null轉爲布爾類型(from undefined and null)

undefined和null在邏輯環境中執行時,都被認爲是false,看下面代碼:設計

// undefined和null都爲false

if (undefined) {
    console.log('true');
} else {
    console.log('false');    // output: false
}


if (null) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

d. 對象轉爲布爾類型(from object)

當對象在邏輯環境中執行時,只要當前引用的對象不爲空,都會被認爲是true。若是一個對象的引用爲null,根據上面的介紹,會被轉換爲false。雖然使用typeof檢測null爲"object",但它並非嚴格意義上的對象類型,只是一個對象空引用的標識。指針

另外,咱們這裏的邏輯環境不包括比較操做符(==),由於它會根據valueOf()和toString()將對象轉爲其餘類型。code

如今咱們來看一下對象類型的示例:

// 字面量對象
var o = {
    valueOf: function() {
        return false;
    },
    toString: function() {
        return false;
    }
};

if (o) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

// 函數
var fn = function() {
    return false;
};

if (fn) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

// 數組
var ary = [];

if (ary) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

// 正則表達式
var regex = /./;

if (regex) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

能夠看到,上面的對象都被認爲是true,不管內部如何定義,都不會影響最終的結果。

正是因爲對象總被認爲是true,使用基礎類型的包裝類時,要特別當心:

// 如下包裝對象都被認爲是true

if (new Boolean(false)) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

if (new Number(0)) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

if (new Number(NaN)) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

if (new String('')) {
    console.log('true');    // output: true
} else {
    console.log('false');
}

根據們上面介紹的,它們對應的基礎類型都會被轉爲false,但使用包裝類實例的時候,引擎只會判斷其引用是否存在,不會判斷內部的值,這一點初學者須要多多注意。固然咱們也能夠不使用new關鍵字,而是顯示的調用其包裝類函數,將這些值轉爲布爾類型:

if (Boolean(false)) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

if (Number(0)) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

if (Number(NaN)) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

if (String('')) {
    console.log('true');
} else {
    console.log('false');    // output: false
}

對於Boolean類,有一個特別須要注意的是,當傳入一個字符串時,它不會去解析字符串內部的值,而是作個簡單地判斷,只要不是空字符串,都會被認爲是true:

if (Boolean('false')) {
    console.log('true');   // output: true
} else {
    console.log('false');
}

if (Boolean('')) {
    console.log('true');
} else {
    console.log('false');  // output: false
}

上面介紹了這麼多,還有幾個例子須要提一下,那就是邏輯非、邏輯與和邏輯或操做符,連用兩個邏輯非能夠把一個值轉爲布爾類型,而使用邏輯與和邏輯或時,根據上面的規則,參與運算的值會被轉換爲相對應的布爾類型:

// 下面幾個轉爲false

var isFalse = !!0;            // false

var isFalse = !!NaN;         // false

var isFalse = !!'';           // false

var isFalse = !!undefined;    // false

var isFalse = !!null;         // false

// 下面都轉爲true

var isTrue = !!3;             // true

var isTrue = !!-3;            // true

var isTrue = !!'0';           // true

var isTrue = !!{};            // true

// 邏輯與

var foo = 0 && 3;             // 0

var foo = -3 && 3;            // 3

// 邏輯或

var foo = 0 || 3;             // 3

var foo = -3 || 3;            // -3

3. 轉爲數字類型(to number)

操做數在數字環境中參與運算時,會被轉爲相對應的數字類型值,其中的轉換規則以下:

i. 字符串類型轉爲數字(from string): 空字符串被轉爲0,非空字符串中,符合數字規則的會被轉換爲對應的數字,不然視爲NaN

ii. 布爾類型轉爲數字(from boolean): true被轉爲1,false被轉爲0

iii. null被轉爲0,undefined被轉爲NaN

iv. 對象類型轉爲數字(from object): valueOf()方法先試圖被調用,若是調用返回的結果爲基礎類型,則再將其轉爲數字,若是返回結果不是基礎類型,則會再試圖調用toString()方法,最後試圖將返回結果轉爲數字,若是這個返回結果是基礎類型,則會獲得一個數字或NaN,若是不是基礎類型,則會拋出一個異常

一個其餘類型的值被轉換爲數字,跟其參與運算的操做符有很密切的聯繫,下面咱們就來詳細介紹:

加號「+」做爲一元操做符(unary)時,引擎會試圖將操做數轉換爲數字類型,若是轉型失敗,則會返回NaN,代碼以下所示:

var foo = +'';            // 0

var foo = +'3';           // 3

var foo = +'3px';         // NaN

var foo = +false;         // 0

var foo = +true;          // 1

var foo = +null;          // 0

var foo = +undefined;     // NaN

上面代碼中,對於不符合數字規則的字符串,和直接調用Number()函數效果相同,但和parseInt()有些出入:

var foo = Number('3px');      // NaN

var foo = parseInt('3px');    // 3

能夠看出,parseInt對字符串參數比較寬容,只要起始位置符合數字類型標準,就逐個解析,直到碰見非數字字符爲止,最後返回已解析的數字部分,轉爲數字類型。

加號「+」做爲二元操做符時,咱們上面也提到過,若是一個操做數爲字符串,則加號「+」做爲字符串鏈接符,但若是兩個操做數都不是字符串類型,則會做爲加法操做符,執行加法操做,這個時候,其餘數據類型也會被轉爲數字類型:

var foo = true + 1;          // 2

var foo = true + false;      // 1

var foo = true + null;       // 1

var foo = null + 1;          // 1

var foo = null + undefined;  // NaN

var foo = null + NaN;        // NaN

上面加法運算過程當中都出現了類型轉換,true轉爲1,false轉爲0,null轉爲0,undefined轉爲NaN,最後一個例子中,null和NaN運算時,是先轉爲0,而後參與運算,NaN和任何其餘數字類型運算時都會返回NaN,因此最終這個結果仍是NaN。

對於undefined轉爲NaN彷佛很好理解,但爲何null會轉爲0呢?這裏也有些歷史淵源的,熟悉C的朋友都知道,空指針實際上是設計爲0值的:

// 空指針的值爲0

int *p = NULL;

if (p == 0) {
    printf("NULL is 0");    // output: NULL is 0
}

編程語言的發展是有規律的,語言之間也存在着密切的關聯,新的語言老是會沿用老的傳統,繼而添加一些新的特性。從上面的例子中,咱們發現,null被轉爲0其實很好理解,一點也不奇怪。

另外,咱們可別忘了減號「-」操做符,當減號「-」做爲一元操做符(unary negation)時,也會將操做數轉換爲數字,只不過轉換的結果與上面相反,合法的數字都被轉爲負值

除加號「+」之外的其餘二元操做符,都會將操做數轉爲數字,字符串也不例外(若是轉型失敗,則返回NaN繼續參與運算):

var foo = '5' - '2';          // 3

var foo = '5' * '2';          // 10

var foo = '5' / '2';           // 2.5

var foo = '5' % '2';          // 1

var foo = '5' << '1';          // 10

var foo = '5' >> '1';          // 2

var foo = '5' ** '2';          // 25


var foo = '5' * true;          // 5

var foo = '5' * null;          // 0

var foo = '5' * undefined;     // NaN

var foo = '5' * NaN;          // NaN

上面的操做符中,位移和求冪操做符平時用的很少,不過在某些場景下(好比算法中)仍是挺實用的。咱們都知道,JavaScript中的數字類型都以浮點型存儲,這就意味着咱們不能想C和Java那樣直接求整除結果,而是經過相關的函數進一步處理實現的,若是經過位移能夠簡化很多,而求冪操做也能夠直接經過求冪運算符算出結果,看下面代碼:

// 浮點型運算
var foo = 5 / 2;                // 2.5

// 整除操做
var foo = Math.floor(5 / 2);    // 2

// 向右移一位實現整除
var foo = 5 >> 1;              // 2

// 求冪函數
var foo = Math.pow(5, 2);       // 25

// 求冪運算
var foo = 5 ** 2;              // 25

除了上面的操做符以外,遞增和遞減操做符也會將操做數轉爲數字,下面之前綴遞增操做符爲例:

var foo = '';

++foo;    // foo: 1


var foo = '3';

++foo;    // foo: 4


var foo = true;

++foo;    // foo: 2


var foo = null;

++foo;    // foo: 1


var foo = undefined;

++foo;    // foo: NaN


var foo = '3px';

++foo;    // foo: NaN

上面就是基本數據類型在數字環境下的轉換規則。對於對象類型,一樣有一套轉換機制,咱們上面也提到了,valueOf()方法和toString()方法會在不一樣的時機被調用,進而獲得相應的返回值,最後根據返回值再進行類型轉換,將其轉爲目標類型。因爲篇幅限制,關於自動類型轉換的後續內容,博主安排在下一篇中講解,敬請期待。

 

參考資料:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators

http://jibbering.com/faq/notes/type-conversion/

http://stackoverflow.com/questions/18808226/why-is-typeof-null-object

相關文章
相關標籤/搜索