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會自動把數字轉換成字元的,不管數字在前還是字串在前,如下:
"2" + 3; // "23" 2 + "3"; // "23"
字串和數字相加結果是字串,字串和數字相加結果是字串,字串和數字相加結果是字串,重要的事情說三遍!!!!!!
此外,需要注意的是,“+”的運算方向是從左到右的,如下:
1 + 2 + "3"; // "33"
這與下面是等價的:
(1 + 2) + "3"; // "33"
相比之下,下面的結果是不一樣的:
1 + "2" + 3; // "123"
但是,隱式型別轉換,有時候,會隱藏一些錯誤的,比如,null會轉換成0,undefined會轉換成NaN。需要注意的是,NaN和NaN是不相等的(這是由於浮點數的精度決定的),如下:
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並不可靠!!!
幸運的是,有一種可靠的並且準確的方法可以檢測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作比較,而不應該直接用真值運算。
相關文章
- JavaScript隱式型別轉換JavaScript型別
- JavaScript 隱式資料型別轉換JavaScript資料型別
- 有趣的JavaScript隱式型別轉換JavaScript型別
- javascript資料型別隱式和顯式轉換詳解JavaScript資料型別
- 【關於Javascript】--- 隱式型別轉換篇JavaScript型別
- JavaScript的隱式型別轉換淺析JavaScript型別
- 「譯」JavaScript 的怪癖 1:隱式型別轉換JavaScript型別
- JavaScript 運算子規則與隱式型別轉換詳解JavaScript型別
- MySQL 隱式型別轉換MySql型別
- javascript中隱私型別轉換JavaScript型別
- javascript 隱式資料型別轉換程式碼例項JavaScript資料型別
- C# 隱式型別轉換(轉載)C#型別
- 如何實現隱式型別轉換型別
- 談談 MySQL 隱式型別轉換MySql型別
- C++隱式類型別轉換C++型別
- C++隱式型別的轉換C++型別
- C++ 隱式類型別轉換C++型別
- 索引失效系列——隱式型別轉換索引型別
- 資料型別的隱式轉換資料型別
- javascript 隱式轉換JavaScript
- Java資料型別的顯式轉換和隱式轉換Java資料型別
- javascript運算中的隱式型別轉換簡單介紹JavaScript型別
- C語言的隱式型別轉換C語言型別
- JavaScript 隱性型別轉換步驟淺析JavaScript型別
- JavaScript 型別轉換JavaScript型別
- c++隱式型別轉換存在的陷阱C++型別
- Oracle隱式型別轉換導致索引失效Oracle型別索引
- 建構函式定義的隱式型別轉換函式型別
- 瞭解JavaScript中的型別轉換JavaScript型別
- 33 個 JavaScript 核心概念系列(三): 顯式 (名義) 與 隱式 (鴨子)型別轉換JavaScript型別
- 徹底理解c++的隱式型別轉換C++型別
- 資料型別隱式轉換導致的阻塞資料型別
- ORACLE中的隱式資料型別轉換(一)Oracle資料型別
- oracle資料型別隱式轉換----- 應急方案Oracle資料型別
- golang 快速入門 [8.4]-常量與隱式型別轉換Golang型別
- Operator運算子過載與Implicit隱式型別轉換型別
- PostgreSQL 原始碼解讀(210)- 隱式型別轉換(func_match_argtypes)SQL原始碼型別
- JavaScript 資料型別轉換JavaScript資料型別