淺論js中toString、valueOf實現機制及意義
這次我們一起來研究下js中toString、valueOf方法的實現機制及意義,以便在未來的工作中能夠更好的對其進行應用。
首先還是做些準備工作吧:
c = console.log;
【1. 最常見也是最好理解的應用場景:值類型數據toString及valueOf方法的使用】
var str = "hello", n = 123, bool = true;
c(typeof(str.toString())+"_"+str.toString())
c(typeof(n.toString())+"_"+n.toString())
c(typeof(bool.toString())+"_"+bool.toString())
分別輸出 “string_hello”、“string_123”、“string_true”
可見,toString方法對於值類型數據使用而言,其效果相當於類型轉換,將原類型強轉為字符串,這一特點符合該方法名“toString”,所以理解上問題不大
再來看下valueOf吧:
c(typeof(str.valueOf())+"_"+str.valueOf())
c(typeof(n.valueOf())+"_"+n.valueOf())
c(typeof(bool.valueOf())+"_"+bool.valueOf())
分別輸出 “string_hello”、“number_123”、“boolean_true”
可見,valueOf方法對於值類型數據使用而言,其效果就相當於返回原數據,用代碼表示即:
c(str.valueOf() === str)
c(n.valueOf() === n)
c(bool.valueOf() === bool)
結果全部為true,再次驗證了我們之前的判斷
小結:
1. toString方法對於值類型數據使用而言,其效果相當於類型轉換,將原類型強轉為字符串;
2. valueOf方法對於值類型數據使用而言,其效果就相當於返回原數據;
【2. 對象類型數據實現toString及valueOf方法】
在js中,object實現了toString方法,因此得到了全面繼承,那麼默認輸出下object.toString\valueOf會發生什麼?
var obj = {};
c(obj.toString());
c(obj.valueOf());
分別輸出 “[object object]” 以及 對象本身
看來valueOf的表現還是一如既往,其效果對於複合對象類型還是相當於返回原數據:
c(obj.valueOf() === obj);就是這麼個意思
不過 toString 方法卻強轉輸出了“[object object]”這麼個玩意兒;
再試試alert輸出下obj對象呢:
alert(obj);
居然也返回了[object object],貌似和剛才執行toString方法的結果一致,那麼不妨大膽猜測下,在alert輸出obj時,其“偷偷”執行了toString方法
再來測試幾個實例:
c(obj + "hello");
c(obj + 22);
分別輸出了 “[object Object]hello”、“[object Object]22”,看來不僅是alert方法,在某種條件下,複合對象類型會自動執行toString方法,進行類型轉換,以實現表達式計算
前面提到過,js中的任意對象都繼承並實現了toString方法,那麼我們重寫下試試呢:
obj.toString = function(){
return 22;
}
c(obj + 3);
c(obj + "3");
分別輸出 25,"223",再次應證了複合對象類型會自動執行toString方法,那麼我們再來重寫下valueOf方法試試呢:
obj.valueOf = function(){
return 12;
}
此時的obj對象,toString和valueOf方法都已被重寫
c(obj + 3);
c(obj + "3");
分別輸出 15,"223",看來在執行數字相加表達式時obj優先自動調用的是valueOf方法,不難看出當複合對象類型在和基礎值類型進行表達式操作時,會基於“場景”自動調用toString或是valueOf方法,以最為恰當的方式,自動完成計算,這點其實也是javascript弱類型語言靈活性的一個體現
再來做個測試:
c(obj == "12");
c(obj === 12);
分別輸出 true、false,貌似很奇怪,實際上是因為全等表達式會比較數據類型,obj不會進行隱式轉換,即不執行toString或valueOf方法而直接參與比較計算,由於兩者數據類型不同,所以返回false;
小結:
1. 複合對象類型在和基礎值類型進行表達式操作時,會基於“場景”自動調用toString或是valueOf方法,以最為"恰當"的方式,自動完成表達式計算
2. 全等表達式會比較數據類型,複合對象類型不會進行隱式轉換,即不執行toString或valueOf方法而直接參與比較計算
3. 深究toString的實現機制】
接下來我們來看看Ecma5對toString內置方法的定義節選:
When the toString method is called, the following steps are taken:
If the this value is undefined, return “[object Undefined]“.
If the this value is null, return “[object Null]“.
...
Return the String value that is the result of concatenating the three Strings “[object ", class, and "]“.
大體翻譯如下:
當調用toString方法時,下列步驟會被執行:
如果this未定義時,返回“[object Undefined]”
如果this為null時,返回“[object Null]”
...
返回三個字符串的拼接字符:"[object",class,"]"
*/
這也就解釋了上面我們對obj進行輸出的效果:"[object" + Object + "]" = "[object Object]"
通過官方說明,可見toString方法實際是返回this的類型,再通過特殊字符串以返回:
c(Object.prototype.toString());
this未定義時,返回“[object Undefined]
c(Object.prototype.toString.call(null));
this為null時,返回“[object Null]
var arr = [1,2];
c(Object.prototype.toString.call(arr));
this為數組,返回“[object Array]
小結:
1. toString方法的內置算法實際是獲取this的底層“類名”,再通過特殊字符串以返回
【4. jquery是如何利用toString實現數據類型判斷的】
開啟jquery源碼,查看type()相關實現流程:
var class2type = {}, core_toString = class2type.toString;
type: function (obj) {
if(obj == null) return String(obj);
return typeof obj === "object" || typeof obj === "function" ? class2type[core_toString.call(obj)] || "object" : typeof obj;
}
可見其方法流程:傳入參數 -> 空值:直接返回null -> 值類型:直接通過typeof返回 -> 複合對象類型:利用obj.toString.call()封裝返回
小結:
1. $.type(obj)方法可返回對象的數據類型,且比typeof更加細化,核心在於算法中巧妙應用了toString內置算法
原文鏈接:http://bbs.itsource.cn/thread-336-1-1.html
感謝源碼時代Web前端學科講師提供此文章!
本文為原創文章,轉載請註明出處!