淺論js中toString、valueOf實現機制及意義

jQuery JavaScript 技術 源碼時代 2017-06-08

這次我們一起來研究下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前端學科講師提供此文章!

本文為原創文章,轉載請註明出處!

相關推薦

推薦中...