JavaScript中的強制型別轉換
將值從一種型別轉換為另一種型別通常稱為型別轉換,這是顯式的情況;隱式的情況稱為強制型別轉換
JavaScript 中的強制型別轉換總是返回標量基本型別值,如字串、數字和布林值
如何理解: 型別轉換髮生在靜態型別語言的編譯階段,而強制型別轉換則發生在動態型別語言的執行時?
1. 如果是靜態語言,比如c等,所有的型別轉換應該都是在編譯階段處理的吧?
2. 如果是動態語言,如js等, 編譯階段會處理型別轉換嗎?
ToString
- 它負責處理非字串到字串的強制型別轉換
- 數字的字串化遵循通用規則,
- 陣列的預設
toString()
方法經過了重新定義
Json字串化
說明
`JSON.stringify()`在將JSON物件序列化為字串時也用到了`ToString`,
但是, JSON字串化並非嚴格意義上的強制型別轉換:
1. 對多數簡單值來說,JSON字串話和`toString()`效果基本相同,只不過序列化的結果總是字串, 所有** 安全的SJON值 **都可以使用`JSON.stringify()`字串化, 安全的JSON值是指能夠呈現為有效JSON格式的值
JSON.stringify(42); // "42"
JSON.stringify("42"); // ""42""
JSON.stringify(null); // "null"
JSON.stringify(true); // "true"
2. 不安全的JSON值
`undefined`, `function`, `symbol`和包含迴圈引用的物件都不符合JSON結構標準, `JSON.stringify()`在物件中遇到`undefined`, `function`, `symbol`時會自動將其忽略, 在陣列中則會返回`null`;
JSON.stringify(undefined); undefined
JSON.stringify(function(){}); // undefined
JSON.stringify([1, undefined, function(){}, 4]); // "[1, null, null, 4]"
JSON.stringify({a: 2, b: function(){}}); // "{"a": 2}""
對包含迴圈引用的物件執行JSON.stringify()會報錯
3. 如果物件中定義了
toJSON
方法, 那麼在呼叫JSON.stringify
之前會預設的隱式呼叫該方法,然後對該方法的返回值使用stringify
,如果要對含有非法JSON值的物件做字串化,可以使用
toJSON
來返回一個安全的JSON值,然後在stringify
中對其JSON字串化 var o = { };
var a = {
b: 42,
c: o,
d: function(){}
};
// 在a中建立一個迴圈引用
o.e = a;
// 迴圈引用在這裡會產生錯誤
// JSON.stringify( a );
// 自定義的JSON序列化
a.toJSON = function() {
// 序列化僅包含b
return { b: this.b };
};
JSON.stringify( a ); // "{"b":42}"
4.
JSON.stringify()
中還可以傳入第二個引數,這個引數是一個陣列或者是函式,用來說明在序列化過程中哪些屬性應該被處理* 如果是一個陣列,那麼應該是一個包含物件中需要處理的屬性的字串陣列
* 如果是一個函式,那麼它會對物件本身處理一次,然後對物件中的各個屬性分別處理一次,
var a = {
b: 42,
c: '42',
d: [1, 2, 3]
}
JSON.stringify(a, ['b', 'c']); // "{"b": 42, "c": "42"}"
JSON.stringify(a, function(k, v){
if(k !== 'c'){
return v;
}
}); // "{"b": 42, "d": [1, 2, 3]}"
這個函式中需要傳遞兩個引數:鍵k和值v, 引數k在第一次呼叫時為
undefined
ToNumber
將非數字值當作數字來使用
其中 true
轉換為1, false
轉換為0, undefined
轉換為NaN
, null
轉換為0, 對字串的處理遵循數字常量的相關規則, 處理失敗時返回NaN
1. 物件如何轉換為數字: 先轉換為對應的基本型別,然後再強制轉換為數字
先檢查是否有`valueOf`方法,如果有並且返回的是**基本型別的值**, 則使用該值進行強制型別轉換,
如果沒有`valueOf()`或者其返回的不是基本值,就使用`toString()`方法的** 返回值 **來進行強制型別轉換
如果`valueOf()`和`toString()`,則會產生`TypeError`錯誤
所以, 使用Object.create(null)
建立的物件是不能進行強制型別轉換的, eg:
var obj = Object.create(null);
obj == 1; //Uncaught TypeError: Cannot convert object to primitive value
obj.valueOf = function(){return 1;}
obj == 1; // true
ToBoolean
在這一部分意識到了之前的一個誤區:先說明一下:
我們知道''==false// true, ''==true; //false
,但是如果字串非空呢?
之前的錯誤**想法是'aa'==true
返回為true, 但後來經過測試發現我錯了,aa==true
和aa==false
返回的都是false
後來想了一下原因: aa
在進行比較時會先轉換為數字,’aa’轉為數字是NaN
, 所以返回的結果是false
言歸正傳:
JS中, 1和true
, 0和false
可以相互轉換,但它們並不是一回事
可以被強制型別轉換為false
的值:
undefined
null
false
+0, -0, NaN
* ""
假值物件
瀏覽器在某些情況下在常規的js語法之外建立的一些外來的值,這些值就是假值物件,在將它們強制型別轉換為布林值時結果就是false
, eg:
document.all == false// false
document.all == true// false
真值就是假值列表之外的值
目前應該只有IE裡面下面程式碼會列印1, 在別的瀏覽器裡面會列印2
if(document.all){
console.log(1);
}else{
console.log(2);
}
真值
真值就是假值列表之外的值
var a = 'false'
var b = '0'
var c = '""'
var d = Boolean(a && b && c);
d; // true
顯示強制型別轉換
字串和數字之間的顯式強制轉換
字串和數字之間的強制轉換是通過String
和Number
函式進行,除此之外也有別的方式:
var a = 42;
var b = a.toString();
var c = '3.14';
var d = +c; // '+'或者'-'運算子單獨使用都可以將運算元轉換為數值,區別在於'-'還會改變運算元的符號
b; //"42";
c; // 3.14
var c = '3.14';
var d = 5+ +c; // 由於'++'會被當作自加運算子處理, 所以應該在中間加一個空格'+ +'
d; // 8.14
日期轉換為數字
+
運算子還有一個作用是可以將日期物件轉化為數字,返回的是Unix時間戳,以微秒為單位
js中作為建構函式的函式如果沒有引數,可以不帶括號的,eg: var a = +new Date
, 此外還可以使用:
var timestamp = (new Date).getTime()
或者:
var timestamp = Date.now() //(推薦的用來獲取當前時間戳的方式)
位運算相關
字位運算只適用與32位整數,運算子會強制運算元使用32位格式。
~x
大致等同於-(x+1)
,只有當x=-1
時, -(x+1)
才等於0
,
字位截除
可以使用~~
來截除數字的小數部分,但這不同與Math.floor
, |
也可以,但是考慮到優先順序,更常用的是~~
;
Math.floor(-49.6); // -50
~~-49.6; // -49
顯示解析數字字串
解析字串中的數字和將字串強制型別轉換為數字是有區別的
var a = '42';
var b = '42px';
Number(a); //42
parseInt(a); //42
Number(b); // NaN
parseInt(b); // 42
解析,顧名思義,用的是
parseInt
或者parseFloat
,強制轉換, 使用的則是
Number
函式解析需要傳入的引數是字串,如果是非字串的話,則會先轉換成字串,解析中還可以傳入第二個引數,代表轉換的進位制,有一個看似無厘頭,實則很正確的一個例子:
parseInt(1/0, 19); // 18
分析如下:
1/0 返回的是 Infinity, 首先,轉換成字串 “Infinity”,也就是說,實際執行的是:
parseInt("Infinity", 19)
, ‘I’在19進位制中代表的是18, 而’n’是沒有意義的,所以解析完I
以後,就返回了,所以結果是18
,其他一些神奇的例子:parseInt(0.000008); // 0
parseInt(0.0000008); // 8
parseInt(false, 16); // 'f'*16 + 'a'= 15 * 16 + 10 = 250
parseInt(parseInt, 16); // 'f'=15
parseInt('0x10'); // 16
parseInt('103', 2); //2
parseInt(78, 8); // 8
顯式轉換為布林值
- 使用
Boolean
函式或者!!
進行ToBoolean
的強制型別轉換
var a = '0'; var b = []; var c = {}; var d = ''; var e = 0; var f = null; var g; Boolean(a); //true Boolean(b); // true Boolean(c); //true Boolean(d); //false Boolean(e); //false Boolean(f); //false Boolean(g); //false
- 顯示
ToBoolean
的一個作用是可以在JSON序列化過程中將不安全的值返回對應的布林值
var a = [1, function(){}, 2, function(){}]; JSON.stringify(a); // "[1, null, 2, null]" JSON.stringify(a, function(k, v){ if(typeof v == 'function'){ return !!v; }else{ return v; } }) // "[1, true, 2, true]"
- 杜絕使用
var b = a ? true : false
(當a涉及了隱式轉換為布林值的時候)
隱式強制型別轉換
字串和數字之間的強制轉換
如果某個運算元是字串或者能夠通過以下步驟轉換為字串的話,+
將進行拼接操作。如果其中一個運算元是物件(包括陣列),則首先對其呼叫 ToPrimitive 抽象操作,
var a = {
valueOf: function(){
return 42;
},
toString: function(){
return 4;
}
}
a + ''; // '42' 呼叫`valueof方法` 先轉換成原始值,在將原始值轉換成字串
String(a); // '4' //String()會呼叫toString方法將其轉換成字串
數字到字串的隱式轉換也是類似的
var a = [1], b = [2];
a - b; // -1
布林值和數字之間的 隱式強制轉換
function onlyOne() {
var sum = 0;
for(var i = 0; i < arguments.length; i++){
if(arguments[i]){
sum += Number(!!arguments[i]);
}
}
return sum == 1;
}
var a = true;
var b = false;
onlyOne(a, b); //true
onlyOne(a, a, b); //false
下面的情況下會發生布林值的隱式強制轉換
if, for, while
? :
三目運算子* 邏輯運算子
||
和’&&’操作符左邊的運算元
||
和&&
js中,這兩個操作符返回的不一定是布林值,它們實際上是選擇兩個運算元中的一個,然後將其返回
可以用它來進行程式碼壓縮:
if(a){
foo()
}
a&&foo()
寬鬆相等 和 嚴格相等
之前在區分==
和===
時,一直覺得說“==
運算子比較值是否相等,===
運算子會檢查值和型別是否相等”很對, 但是這次看書上說,
==
允許在相等比較中進行強制型別轉換,而===
不允許
覺得這種說法更加準確
-
字串和數字之間的相等比較
無論字串是在操作符的左邊還是右邊,所採取的操作都是將字串轉換成數字進行比較 -
其他型別和布林型別之間的相等比較
會將其他型別轉換成數值,然後進行比較,
eg:
var x = true, y = '42'; x == y; // false
-
null
和undefined
之間的相等比較
null
和undefined
之間的==
比較也涉及隱式強制型別轉換
var a = null;
var b;
a == b; //true
a == null; //true
b == null; // true
a == false; //false
b == false; //false
4. 物件和非物件之間的相等比較
物件和標量基本型別進行相等比較是,物件會先轉換成原始值,然後進行比較
var a = 42;
var b = [42];
a == b; //true
如果其中的物件是原始值的封裝物件,那麼在比較時,物件轉換成原始值的過程其實也就是物件的解封裝過程,但有些情況需要注意:
1.
undefined
和null
是沒有封裝物件的2.
NaN
雖然有封裝物件,但是解封回來的原始值也是NaN
,而NaN
是不等於NaN
的eg:
var a = null;
var b = Object(a);
a == b; //false
var c = undefined;
var d = Object(c);
c == d; //false
var e = NaN;
var f = Object(e);
e == f; //false
最後,是一些比較的易錯點
'0' == false; //true
false == 0; //true
false == ''; //true
false == []; //true
'' == 0; //true
'' == []; //true
[] == ![]; //true, 首先進行![]的隱式轉換,先將`[]`轉換成布林值,為`true`,然後取反是`false`,而`[] == false`是true
'' == [null]; //true, 注意`[null]`字串話後是空字串`''`
0 == '\n'; // true; 原因在於: ''或'\n'等空字串在`ToNumber`的過程中被轉換成`0`
抽象關係比較:
- 如果比較雙方都是字串,那麼進行字串間的比較
- 否則,會將比較雙方都轉換成原始值,如果轉換結果中有非字串,那麼就都轉換成數字進行比較
然後裡面有一些比較詭異的事情:
var a = {b: 42};
var b = {b: 43};
a < b; //false a == b; //false a > b; //false
a <= b; //true
a >= b; //true
分析如下:
首先比較雙方都不是字串,先將其都轉換成原始值:
a == ‘[object Object]’, b == ‘[object Object]’;
那麼不應該是 a == b
嗎?
因為根據規範,js中的比較是這麼處理的:
a <= b;
被處理為 !(b<a)
, 因為 b < a
的結果是false
, 所以a<=b
返回true
相關文章
- JavaScript強制型別轉換的背後操作JavaScript型別
- PHP 型別轉換&&型別強制轉換PHP型別
- JS在if中的強制型別轉換JS型別
- 強制型別轉換型別
- java型別轉換與強制型別轉換(轉)Java型別
- javascript強制型別轉換與操作符JavaScript型別
- Javascript基礎之-強制型別轉換(三)JavaScript型別
- Javascript基礎之-強制型別轉換(一)JavaScript型別
- 強制型別轉換之(==)型別
- C++ 的強制型別轉換C++型別
- C++強制型別轉換C++型別
- 造型與強制型別轉換型別
- C++ 中四種強制型別轉換的區別C++型別
- 引用型別變數的強制轉換型別變數
- C C++ 強制型別轉換C++型別
- 深入淺出說強制型別轉換型別
- c++四種強制型別轉換C++型別
- 在JavaScript中也玩變數型別強行轉換JavaScript變數型別
- 瞭解JavaScript中的型別轉換JavaScript型別
- 強制型別轉換(int)、(int&)和(int*)的區別型別
- javascript中隱私型別轉換JavaScript型別
- 沿著平滑的曲線學會 JavaScript 中的隱式強制型別轉換(基礎篇)JavaScript型別
- JavaScript 型別轉換JavaScript型別
- String.valueOf和強制型別轉換(String)的區別型別
- 沿著平滑的曲線學會 JavaScript 中的隱式強制型別轉換(實戰應用篇)JavaScript型別
- 說說JavaScript的型別轉換JavaScript型別
- 淺談JavaScript的型別轉換JavaScript型別
- JavaScript隱式型別轉換JavaScript型別
- JavaScript 資料型別轉換JavaScript資料型別
- javascript資料型別轉換JavaScript資料型別
- JavaScript之number型別的數值轉換成某某進位制JavaScript型別
- C++中的向上型別轉換和向下型別轉換C++型別
- swift 3.0:as語法與強制型別轉換的一些理解Swift型別
- C++開發必看四種強制型別轉換的總結C++型別
- java中的型別轉換Java型別
- 有趣的JavaScript隱式型別轉換JavaScript型別
- (十五)C++學習 | 強制型別轉換 異常處理C++型別
- 選擇is或者as操作符而不是做強制型別轉換型別