js-看懂隱式轉換toString() 和 valueOf()

js隱式類型轉換

JavaScript數據類型非常弱弱類型語言)在使用算術運算符時,運算符兩邊數據類型可以任意,比如,一個字符串可以和數字相加。之所以不同的數據類型之間可以做運算,是因爲JavaScript引擎運算之前會悄悄的把他們進行了隱式類型轉換

數值類型和布爾類型的相加

5 + true // 6

結果是一個數值類型!上面的運算不會因爲運算符兩邊的數據類型不一致而導致報錯,在JavaScript中,只有少數情況下,錯誤類型纔會導致出錯,比如調用非函數,或者讀取null或者undefined的屬性時,如下

"hello"(1); // Uncaught TypeError: "hello" is not a function
null.x; // Uncaught TypeError: Cannot read property 'x' of null

字符串和數字相加

字符串和數字相加,JavaScript會自動把數字轉換成字符的,不管數字在前還是字符串在前

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

字符串數字相加結果是字符串

需要注意:“+”的運算方向從左到右

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

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

下面跟上面對比

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

隱式類型轉換隱藏一些錯誤

隱式類型轉換,某些情況下,會隱藏一些錯誤,比如,null會轉換成0undefined會轉換成NaN
需要注意的是,NaN和NaN不相等(這是由於浮點數的精度決定的),如下:

let x = NaN;
x === NaN; // false

isNaN()

isNaN–MDN
當算術運算返回一個未定義的或無法表示的值時,NaN就產生了。但是,NaN並不一定用於表示某些值超出表示範圍的情況。將某些不能強制轉換爲數值的非數值轉換爲數值的時候,也會得到NaN。

例如,0 除以0會返回NaN —— 但是其他數除以0則不會返回NaN。

雖然,JavaScript提供了isNaN方法來檢測某個值是否爲NaN,但是,這也不太精確的,因爲,在調用isNaN函數之前,本身就存在了一個隱式轉換的過程,它會把那些原本不是NaN的值轉換成NaN的,如下:

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

如果isNaN函數的參數不是Number類型, isNaN函數會首先嚐試將這個參數轉換爲數值,然後纔會對轉換後的結果是否是NaN進行判斷。因此,對於能被強制轉換爲有效的非NaN數值來說(空字符串和布爾值分別會被強制轉換爲數值0和1),返回false值也許會讓人感覺莫名其妙。

與 JavaScript 中其他的值不同,NaN不能通過相等操作符(== 和 ===)來判斷 ,因爲 NaN == NaN 和 NaN === NaN 都會返回 false。

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

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

簡單封裝下

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

對象的隱式轉換

對象可以轉換成原始值,最常見的方法就是把它轉換成字符串

"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) { ... }

// 如下
let z
z === undefined // true

總結

  1. 類型錯誤有可能會被類型轉換所隱藏。
  2. “+”既可以表示字符串連接,又可以表示算術加,這取決於它的操作數,如果有一個爲字符串的,那麼,就是字符串連接了。
  3. 對象通過valueOf方法,把自己轉換成數字,通過toString方法,把自己轉換成字符串。
  4. 具有valueOf方法的對象,應該定義一個相應的toString方法,用來返回相等的數字的字符串形式。
  5. 檢測一些未定義的變量時,應該使用typeOf或者與undefined作比較,而不應該直接用真值運算。

謝謝你閱讀到了最後
期待你,點贊、評論、交流

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章