有趣的JavaScript隱式類型轉換

JavaScript的數據類型是很是弱的(否則不會叫它作弱類型語言了)!在使用算術運算符時,運算符兩邊的數據類型能夠是任意的,好比,一個字符串能夠和數字相加。之因此不一樣的數據類型之間能夠作運算,是由於JavaScript引擎在運算以前會悄悄的把他們進行了隱式類型轉換的,以下是數值類型和布爾類型的相加:安全

3 + true; // 4

結果是一個數值型!若是是在C或者Java環境的話,上面的運算確定會由於運算符兩邊的數據類型不一致而致使報錯的!可是,在JavaScript中,只有少數狀況下,錯誤類型纔會致使出錯,好比調用非函數,或者讀取null或者undefined的屬性時,以下:函數

"hello"(1); // error: not a function
null.x; // error: cannot read property 'x' of null

多數狀況下,JavaScript都不會出錯的,而是自動的進行相應的類型轉換。好比-, *, /,和%等算術運算符都會把操做數轉換成數字的,可是「+」號就有點不同了,有些狀況下,它是算術加號,有些狀況下,是字符串鏈接符號,具體的要看它的操做數,以下:測試

2 + 3; // 5
"hello" + " world"; // "hello world"

可是,若是字符串和數字相加,會是怎樣的結果呢?JavaScript會自動把數字轉換成字符的,無論數字在前仍是字符串在前,以下:spa

"2" + 3; // "23"
2 + "3"; // "23"

字符串和數字相加結果是字符串,字符串和數字相加結果是字符串,字符串和數字相加結果是字符串,重要的事情說三遍!!!!!!code

此外,須要注意的是,「+」的運算方向是從左到右的,以下:
1 + 2 + "3"; // "33"

這與下面是等價的:對象

(1 + 2) + "3"; // "33"

相比之下,下面的結果是不同的:blog

1 + "2" + 3; // "123"

可是,隱式類型轉換,有時候,會隱藏一些錯誤的,好比,null會轉換成0,undefined會轉換成NaN須要注意的是,NaN和NaN是不相等的(這是因爲浮點數的精度決定的),以下:ip

var x = NaN;
x === NaN; // false

雖然,JavaScript提供了isNaN來檢測某個值是否爲NaN,可是,這也不太精確的,由於,在調用isNaN函數以前,自己就存在了一個隱式轉換的過程,它會把那些本來不是NaN的值轉換成NaN的,以下:字符串

isNaN("foo"); // true
isNaN(undefined); // true
isNaN({}); // true
isNaN({ valueOf: "foo" }); // true

上面代碼,咱們使用isNaN來測試後,發現字符串,undefined,甚至對象,結果都返回真!!!可是,咱們總不能說他們也是NaN吧?總而言之,得出的結論是:isNaN檢測NaN並不可靠!!!it

幸運的是,有一種可靠的而且準確的方法能夠檢測NaN。咱們都知道,只有NaN是本身不等本身的,那麼,咱們就以使用不等於號(!==)來判斷一個數是否等於自身,從而,能夠檢測到NaN了,以下:

var a = NaN;
a !== a; // true
var b = "foo";
b !== b; // false
var c = undefined;
c !== c; // false
var d = {};
d !== d; // false
var e = { valueOf: "foo" };
e !== e; // false

咱們也能夠把這種模式定義成一個函數,以下:

function isReallyNaN(x) {
    return x !== x;
}

OK,NaN的檢測方法就是這麼簡單,咱們下面繼續討論對象的隱式轉換!

對象是能夠轉換成原始值的,最多見的方法就是把它轉換成字符串,以下:

"the Math object: " + Math; // "the Math object: [object Math]"
"the JSON object: " + JSON; // "the JSON object: [object JSON]"

對象轉換成字符串是調用了他的toSting函數的,你能夠手動的調用它來檢測一下:

Math.toString(); // "[object Math]"
JSON.toString(); // "[object JSON]"

相似的,對象也是能夠轉換成數字的,他是經過valueOf函數的,固然,你也是能夠自定義這個valueOf函數的,以下:

"J" + { toString: function() { return "S"; } }; // "JS"
2 * { valueOf: function() { return 3; } }; // 6

若是,一個對象同時存在valueOf方法和toString方法,那麼,valueOf方法老是會被優先調用的,以下:

var obj = {
    toString: function() {
        return "[object MyObject]";
    },
    valueOf: function() {
        return 17;
    }
};
"object: " + obj; // "object: 17"

可是,多數狀況下,這都不是咱們想要的,通常的,儘量使valueOf和toString表示的值相同(儘管類型能夠不一樣)。 

最後一種強制類型轉換,咱們經常稱之爲「真值運算」,好比,if, ||, &&,他們的操做數不必定是布爾型的額。JavaScript會經過簡單的轉換規則,將一些非布爾類型的值轉換成布爾型的。大多數的值都會轉換成true,只有少數的是false,他們分別是 false, 0, -0, "", NaN, null, undefined,由於存在數字和字符串以及對象的值爲false,因此,直接用真值轉換來判斷一個函數的參數是否傳進來了,這是不不太安全的。好比,有一個能夠具備默認值得可選參數的函數,以下:
function point(x, y) {
if (!x) {
    x = 320;
}
if (!y) {
    y = 240;
}
    return { x: x, y: y };
}

這個函數會忽略任何的真值爲假的參數的,包括0,-0;

point(0, 0); // { x: 320, y: 240 }

檢測undefined的更加準確的方法是用typeof操做:

function point(x, y) {
if (typeof x === "undefined") {
    x = 320;
}
if (typeof y === "undefined") {
    y = 240;
}
    return { x: x, y: y };
}

這種寫法,能夠區分開0和undefined的:

point(); // { x: 320, y: 240 }
point(0, 0); // { x: 0, y: 0 }

另一種方法是利用參數跟undefined做比較,以下:

if (x === undefined) { ... }

總結:

1. 類型錯誤有可能會被類型轉換所隱藏。
2. 「+」既能夠表示字符串鏈接,又能夠表示算術加,這取決於它的操做數,若是有一個爲字符串的,那麼,就是字符串鏈接了。
3. 對象經過valueOf方法,把本身轉換成數字,經過toString方法,把本身轉換成字符串。
4.具備valueOf方法的對象,應該定義一個相應的toString方法,用來返回相等的數字的字符串形式。
5.檢測一些未定義的變量時,應該使用typeOf或者與undefined做比較,而不該該直接用真值運算。
相關文章
相關標籤/搜索