JavaScript系列文章:自動類型轉換

jerry_yang 8年前發布 | 6K 次閱讀 JavaScript開發 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 ,代碼如下:

// 空字符串為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()將對象轉為其他類型。

現在我們來看一下對象類型的示例:

// 字面量對象
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. 對象類型轉為數字(from object): valueOf()方法先試圖被調用,如果調用返回的結果為基礎類型,則再將其轉為數字,如果返回結果不是基礎類型,則會進一步調用返回值的toString()方法,最后再試圖將返回結果轉為數字

iv. null被轉為0,undefined被轉為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

 

來自:http://www.cnblogs.com/liuhe688/p/5918589.html

 

 本文由用戶 jerry_yang 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!