JS 強制類型轉化


  在Js中, 強制類型轉化分為兩種情況: 一種是引用類型轉化基本類型, 如數組轉化成數字;一種是兩種不同基本類型之間的轉化,如字符串轉化為數字。你不能將基本類型轉化成引用類型,比如,不可能把數字轉化為數組。 基本類型之間的轉化相對容易,引用類型轉化為基本類型則要復雜的多,轉化又分為兩種情況,轉化為字符號或轉化為數值

  當引用類型轉化為字符串的時候,JS會先調用引用類型的toString 方法,看它能不能返回基本類型,如果能,則使用該基本類型,如果不能,則再調用引用類型的valueOf()方法,如果valueOf方法能返回基本數據類型則使用該基本數據類型 ,如果不能,就會拋出TypeError錯誤。你可能還記得 字符串 "[object object]"

var arr = [5, 5]; 
console.log(arr + '');  // "5, 5"  調用數組的toString()方法

  當引用類型轉化為數值的時候,JS則先調用引用類型的valueOf()方法,同樣是看它能不能返回基本類型,如果能就用,如果不能,則繼續調用toString()方法,如果toString()方法也不能返回基本類型,則拋出TypeError錯誤。

console.log(+ new Date());  // 時間毫米數1513860979488, 數值類型

  知道了轉化規則,我們就可以利用它來轉化我們自定義的對象,但有的時候,轉化並沒有我們上面看到的那么簡單,看下面的代碼

var Money = function(val, sym) {
    this.currentSysmbol = sym;
    this.cents = val;
}
var dollar = new Money(100, '$');

console.log(+dollar);  // NaN
console.log("Total" + dollar);  // '[object, object]'

  得到的結果並沒有什么意義,我們來給它提供toString, valueOf()方法

Money.prototype.toString = function() {
    return this.currencySymbol + (this.cents / 100).toFixed(2);
}
Money.prototype.valueOf = function() {
    return this.cents
}
console.log(+dollar);  // number 100
console.log("" + dollar);   // string 100

  這時你會發現一個問題:  " " +dollar, 按理說,它應該調用toString 方法,返回$1.00, 但它卻返回了100, 證明它這里調用的是valueOf()方法,而不是toStirng()方法,這和我們的規則出現了不一致。 但是當我們把dollar 用數組包起來的時候,它返回調用的是toString()方法

console.log("" + [dollar])  // $1.00

  這時要看一下js的標准,

  

  轉化成基本類型的抽象方法ToPrimitive 接受一個必選的參數(input argument) ,就是一個引用類型object和一個可選的參數(PreferredType), 然后把引用類型轉化成基本類型。如果一個引用類型能夠返回不止一個基本類型時,它就可能需要PreferredType 來決定返回哪一個。這個方法是怎么把引用類型轉化成基本類型的?

   

  轉化成基本類型,就是標准中所說的return a default value, 需要調用引用類型內置的方法,然后給它傳遞可選的參數(hint PreferredType). 如果再深入挖掘標准, 你會發現所說的方法,就是toString()和valueOf(), 返回字符串或數值, 到底是返回哪種基本類型取決於傳入的可選參數hint PreferredType. 如果hint參數沒有傳遞的話,它默認返回數值number。請下面的標准

  

  這也就解釋了("" + dollar)為什么沒有調用我們定義的toString()方法,而是調用valueOf()方法了, 因為我們沒有提供hint PreferredType參數,默認返回了數值。基本操作符並不能直接用來計算引用類型,它只能計算基本類型, 當計算引用類型的時候,它進行強制類型轉化,而這種轉化到底轉化成哪種基本類型,是js自己做決定。那我們能不能提供一個hint Preferred 參數,把決定權拿回到我們手里。很不幸,js並不沒有提供這樣的機制,也就是說,我們不能提供一個hint 參數給我們自己定義的對象

  但是當我們把dollar用數組括起來,它卻調用了我們自定義的toString()方法. "" + [dollar] 返回 $1.00, 這又怎么解釋呢? 這涉及了Js的內置對象。

  

    對於內置對象的來說, 它必須返回一個基本類型。我們把dollar用數組包括起來,但由於涉及到加號操作,所以[dollar] 要轉化成基本類型,根據規則,它調用的valueOf()方法,但是數組的valueOf方法返回數組本身,不是基本類型。所以再調用數組的toString()方法,數組的toString()方法就是數組的每一項都調用toString()方法,然后再拼接起來,形成一個大的字符串,所以這里調用的是dollar 對象的toString()方法。

  我們可以模擬一個ToPrimitive的方法 

var ToPrimitive = function(obj) {
    var funct, functions, val, _i, _length;

    functions = ['valueOf', 'toString'];

    if(typeof obj === 'object') {
        // js 對Date日期對象作了區別處理, 先調用toString, 再調用toValue
        if (obj instanceof Date) {
            functions = ['toString', "valueOf"];
        }
        for(_i = 0, _length=functions.length; _i < _length; _i++){
            funct = functions[_i];
            // valueof 或 toString 方法都是函數時才調用
            if (typeof obj[funct] === 'function') {
                val = obj[funct]();
                if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
                    return val;
                }
            }
        }
        // 如果兩個方法都不能轉化成基本數據類型,則拋出錯誤
        throw new Error('DefualtValue is ambuiguos')
    }
    return obj;  // 如果傳遞進來的參數不是引用類型,則返回
}

  現在我們已經明白了js的類型轉化,再做一個復雜的例子加深一下印象, 下面這行代碼輸出多少

console.log(++[[]][+[]]+[+[]])

  1, 先計算數組內的元素+[], 前面的加號肯定是要數組轉化成基本類型,數組轉化成基本類型,只能調用它的toString()方法,空數組轉化為空字符串'', +'' 表示對空字符串轉化為數字,它這里調用的是Number()函數,所為轉為化0, 現在變成了

console.log(++[[]][0]+[0])

  2, 加號左邊[[]][0], 表示它取的是[[]] 里面的第0個位置的元素,[[]] 表示它是一個二維數組,數組里面包括數組,所以它的第0個位置上的元素還是數組,變成了

console.log(++[]+[0])

  3 , 左邊是++[], 還是對數組進行基本類型轉化,上面說了 [] 轉化為空字符串,++‘’ 則變成了數字1,

console.log(1+[0])

  4, 右側還是要做類型轉化, 數組[0] 調用toString,則變成了字符串‘0’ 

console.log(1+'0')

  5, 最終的結果就是字符串‘10’


免責聲明!

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



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