js強制類型轉換規則


這篇隨筆記錄一下js中數據的各種類型轉換的規則,雖然很基礎,但是重新過一遍會發現有些規范還是挺意想不到的

首先介紹一下ToString, ToNumber, ToBoolean 的轉換規則

1、ToString

  規則1:null 轉換為 “null” , undefined 轉換為 “undefined” , true 轉換為 “true” ;

  規則2:普通對象,除非自定義,否則用 toString();

      數組的toString()方法,先把所有單元字符串化,然后再用“,”連接;[1,2,3,4]  // “1,2,3,4”;

2、ToNumber

  規則1:布爾值 true 轉換為 1, false 轉換為 0; undefined 轉換為 NaN; null 轉換為 0;字符串處理失敗時返回NaN;

  規則2:以 0開頭地十六進制數會被當成十進制;

  規則3:對象會先被處理成相應地基本類型值,再按照值類型做相應處理;

      對象做ToPrimitive操作規則:先valueOf(), 如果valueOf的結果不是基本類型,再進行 toString();如果均不返回基本類型,則報TypeError;

      使用Object.create(null) 創建的對象,無valueOf 跟 toString 方法,故不能被強制類型轉換

  規則4:空數組和空字符串,都會被轉換為0:

Number("") // 0;
Number([]) // 0;

3、ToBoolean

  規則1:undefined,null,false,+0,-0,NaN,“” 會被轉換為false;但是對應的封裝對象為true

  規則2:js中 document.all  如果用在 if 語句中,會被轉換為false;此條規則需要注意的是,在ie<= 10 的瀏覽器中,會返回true;

  規則3:除規則1和規則2,其他都返回true

Boolean(undefined) // false;
Boolean(null); // false;
Boolean(false); // false;
Boolean(+0); // false;
Boolean(-0); // false;
Boolean(""); // false;

const a = new Boolean( false) ;  // true
const b = new Number(0); // true
const c = new String("") ;  // true

if(document.all){
    // ie10 及 以下,會被打印; console.log('document.all 在if語句中被強制類型轉換,但轉換的結果為false,這條語句不會被打印'); }

  

JSON.stringify 的規則與 強制類型轉換關聯,故也總結到這里:

  JSON字符串化簡單數值時,規則與toString大致相同;但是序列化的結果總是字符串:JSON.stringify("42")  //  ""42"";

  undefined、function、symbol 和包含對象間循環引用的,都屬於不安全的JSON值,這些值會被忽略,數組種則會返回 null,儀保證單元位置不變

  例如:JSON.stringify([1,undefine,function(){},2])   // [1, null, null, 2];

     JSON.stringify( {a:1, b: undefine, c: function(){} } )  // {a:1};   此特性可用於發起請求時的無效參數過濾;

  可自定義toJSON 函數,在JSON序列化時會被調用;該函數應該返回一個能夠被字符串化的安全的JSON值,而不是直接返回一個字符串,因為處理邏輯是對toJSON返回的字符串做字符化;

  JSON.stringify 支持傳入可選參數replacer,用來指定對象序列化過程中屬性的處理邏輯:

    a、當replacer為數組時, JSON.stringify 只序列化數組中包含的屬性,例如:    

var a = {
      name:  "Lily",
      age:  12 ,
      hobby: ["basketball", "football"]        
}
JSON.stringify(a, ["name", "age"]);    //  "{"name": "Lily" , "age": 12}"

// 這個特性可以用來提取對象中指定屬性

    b、當replacer為函數時, 函數會對將要被序列化的對象做一個遍歷,入參為key 和 value,可在函數中寫處理邏輯:

var a = {
      name:  "Lily",
      age:  12 ,
      hobby: ["basketball", "football"]        
}
JSON.stringify(a, function(key, value){    //   "{"name":"Lily","hobby":["basketball","football"]}"
    if(key !== "age") return value; 
});

    這里有一個細節: 當replacer函數第一次被調用時,是對對象本身的調用,key 為 undefined;

    屬性的值為數組時,數組中的值也將被遍歷,此時key為數組的索引,value為數組的單元值;

接下來,我們看下一些操作符在強制類型轉換中的影響:

1、+  操作符

  +String : 會把字符串轉換為數字;

  +Date:會把時間對象轉換為時間戳;同樣使用 Date.now(); new Date().getTime()也可以得到時間戳;

  String + other :首先 other 會被轉換為字符串,再進行拼接操作;下面看一個例子:

 {} + []  //  0   此處 {} 出現在語句的開始,會被解析為代碼塊,[]被轉換為數字0 

 [] + {}  // "[Object Object]"   此處遵循字符串相加規則,[] 為 “”, {}為 "[Object Object]"

  Number + Boolean:Boolean 會先被轉換為Number類型,再進行相加操作

 -   x  /  等操作符,只針對Number類型有效,故這三個操作符會把操作數轉換為數字類型;

2、位運算符 ~ 和 |   位運算符只適用於32位整數,故操作數會被ToInt32強制轉換為32位格式;非數字類型的值會先通過ToNumber轉換為 數字類型;

  ~ 運算(字位操作“非”)返回2的補碼   ~x 大致等於  -(x+1);在~運算中,只有 x 為 -1 時,才會返回 0;這個特性可以用在將-1做為哨位值的運算中,避免暴露底層實現細節(抽象滲漏)

var a = "Hello world";
if(~a.indexOf("H")){
     // 找到匹配  
}

       | 運算符(字位操作“或”)

0 | -0    // 0
0 | NaN   // 0
0 | Infinity  // 0
0 | -Infinity  // 0

       !運算符 :非運算符常出現在布爾值的隱式轉換中;

3、&& 和 || 運算 : 該邏輯運算(更形象地可以說是操作數選擇器運算)會返回其中一個操作數:

  a && b  : 首先對第一個操作數做條件判斷,結果為 true, 則返回 第二個操作數 b;如果為 false, 則返回第一個操作數a;

  a || b :  首先對第一個操作數做條件判斷,結果為true,返回第一個操作數,條件判斷結果為 false, 返回第二個操作數;

  這里有一個細節,a || b 的條件判斷語句中,如果 a為true,則不會再繼續執行 b;在這個特性下,可以做個小小的優化:

a ? a : b   可被優化為: a || b;

  另外一個優化點就是我們熟知的短路寫法: b && b();   var a = b || 'default'; 等。

4、== 和 === 

  寬松相等(==)和嚴格相等(===)的區別在於,寬松相等允許在相等比較中進行強制類型轉換。他們都會對值類型進行檢查,區別在於類型不同時他們的處理方式。

  以下是 == 操作的一些規則:

  規則1:NaN 不等於 NaN;+0  等於 -0;

  規則2:對象的比較方式為:兩個對象指向同一個值時,即為相等,不發生強制類型轉換

  強制轉換規則:

    a、字符串和數字之間:字符串先被轉換成數字,再進行比較

    b、其他類型和布爾值之間:布爾值要先被轉換為數字,再進行比較

    c、null 和 undefined之間的相等比較:null == undefined  // 返回true

    d、對象和非對象之間的比較:對象會被先轉換為標量基本類型(字符串/數字/布爾值),然后再進行比較;

  下面看一些容易被迷惑的例子:

var a = null;
var b = Object(null);
a == b  // false;  null 沒有對應的對象類型,故 d 相當於Object()創建的一個普通對象

var c = undefined;
var d = Object(d);
c == d  // false    undefined 沒有對應的對象類型,故 d 相當於Object()創建的一個普通對象
var e = NaN; var f = Object(NaN); e == f // false f被包裝為Number類型,拆封后為NaN,NaN == NaN // false

"0" == false // true 根據規則 b , 布爾值跟其他值比較,要先把布爾值轉換為數字,false -> 0; "0" == 0 為true
false == "" // true 根據規則b, false 被轉換為 0; 根據規則a,字符串 "" 也被轉換為 0
false == [] // true [] 先調用 valueOf,返回數組本身,繼續調用 toString, 返回空字符串 ""; false == "" 為 true
false == {} // false {} 先調用 valueOf,返回 {} ; 繼續調用 toString, 返回字符串 "[object Object]" , false == "[object Object]" 為 false
[] == ![] // true 首先計算 ![] 根據布爾值的轉換規則,[] 為 true , 那么 ![] 則為false 即 [] == false 結果為 true
2 == [2] // true [2] 調用 valueOf 返回數組對象[2], 繼續調用toString, 返回 "2" , 2 == "2" 結果為 true
"" == [null] // true 同樣valueOf 返回數組對象 [null], toString 返回 "" ;
0 == "\n" //true 空字符串,或者空格,都會被ToNumber轉換為0
"true" == true // false 這個簡單, 布爾值會先被轉換為Number, "true" == 1 結果為 false

5、關於 <= 和 >=

  這次最讓人意外的就是這倆操作符,我們來看個例子:

var a = {b: 42};
var b = {b: 43};
a < b  // false; 
a > b // false;
a == b // false;
a <= b  // true;
a >= b  // true;

 所以 <=  和 >=  為什么都為true? 

  首先,a 和 b都會先被處理成字符串 "[object Object]", 故  >  和 <  比較都為 false;根據對象的 == 運算規則,地址一致為相等, 故 == 比較也為false;

  根據 規范  a <= b 會被處理成 b < a, 然后將結果反轉。因為 b < a 為 false, 故 a <= b 為 true;也就是說,js中的 <=  其實是 “不大於” 的意思。。。

另外,還有一些對數據類型做了轉換的函數,也值得關注

1、parseInt() 顯示解析數字字符串 

  和 Number的顯示轉換 不同,解析允許字符串中含有非數字字符,而Number顯示轉換則不允許:

Number("42px"); // NaN;   顯示轉換
parseInt("42px"); // 42   顯示解析

Number(""); // 0
parseInt("") // NaN

  parseInt針對的是字符串值,如果向parseInt傳入了一些奇奇怪怪的參數,那轉換結果也是會出乎意料的:

  parseInt先將參數強制類型轉換為字符串再進行解析

parseInt(0.000008)   // 0   首先被轉換為字符串“0.000008”;然后被解析出 0;
parseInt(0.0000008)   // 8  首先被轉換為字符串“8e-7”,  然后被解析出 8
parseInt(false,  16)   //  250   "false" 進行 16進制轉換,前面的兩個字母 “fa” 在16進制的字母內,被轉換為250
parseInt(parseInt, 16)   // 15  首先被解析為字符串“function (){}...”  首字母 “f” 以十六進制轉換規則,被轉換為十進制 15  
parseInt("0x10")   //  16  0x開頭為16進制,所以只看后兩位0*16^0+1*16^1=16,所以0x10=16
parseInt("103", 2)   // 2  字符串103 以2進制規則轉換,前兩位 "10" 符合二進制數,被轉換為 2, "3" 不在二進制表達范圍內,故被舍棄,最終轉換為十進制的結果為2; 

最后,提一下es6中符號類型,它的強制轉換都必須是顯式的,但轉換為布爾值除外;並且符號不能夠被強制類型轉換為數字:

var s1 = Symbol("cool");
String(s1)  // "Symbol(cool)";
var s2 = Symbol("not cool");
s2 + "" ; // TypeError

Boolean(s1) ; // true
!!s1 // true
if(s1){
  console.log('這條語句可以被執行');
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM