javascript parseInt 大改造

來源:互聯網
上載者:User

還隱約記得得知了來龍去脈,為自己掌握了一個經驗而歡呼雀躍。

還隱約記得被這同一問題折磨了無數次後,無奈與痛下決心的心境。

首先我必須感謝那些即使這個問題我強調過無數次,也依然反覆重複類似錯誤的人們。
沒有他們反覆犯錯的鼓勵,或許我不會認真考慮這個問題的解決方案。
其次,必須感謝《JavaScript進階程式設計》的作者和譯者。
在這裡我得到瞭解決該問題的啟示,不然我依然要每每強調使用parseInt時應注意什麼。

同時,希望在這裡不僅僅留下一個解決方案。
解決問題的思路與想法,以及對問題舉一反三的經驗在這裡保留一下。

問題:
很久以前發生的問題不想再痛苦的回憶。
這次的問題很簡單。兩個月份比較的時候,因為月份是從字串中抽取出來的, 於是用parseInt轉換了一下。
結果parseInt("08")之後結果是 0
原因請參看以下《JavaScript進階程式設計》19~20頁對 parseInt函數的講解。

parseInt() 方法還有基模式,可以把二進位、八進位、十六進位或其他任何進位的字串轉換成整數。
基是由 parseInt() 方法的第二個參數指定的,所以要解析十六進位的值,需如下調用 parseInt() 方法: 複製代碼 代碼如下:var iNum1 = parseInt("AF", 16); //返回 175

當然,對二進位、八進位甚至十進位(預設模式),都可以這樣調用 parseInt() 方法:複製代碼 代碼如下:var iNum1 = parseInt("10", 2); //返回 2
var iNum2 = parseInt("10", 8); //返回 8
var iNum3 = parseInt("10", 10); //返回 10

如果十進位數包含前置 0,那麼最好採用基數 10,這樣才不會意外地得到八進位的值。例如:

var iNum1 = parseInt("010"); //返回 8
var iNum2 = parseInt("010", 8); //返回 8
var iNum3 = parseInt("010", 10); //返回 10

在這段代碼中,兩行代碼都把字元 "010" 解析成一個數字。

第一行代碼把這個字串看作八進位的值,解析它的方式與第二行代碼(聲明基數為 8)相同。最後一行代碼聲明基數為 10,所以 iNum3 最後等於 10。

初試身手:
以前的解決方案是讓大家都拋棄 parseInt函數,全部以parseFloat來替換。
但是作為人“孰能無忘”?
最好的辦法莫過於保留parseInt的“形”,廢了parseInt的“神”。
於是我想到了《JavaScript進階程式設計》87~88頁關於“重定義已有方法”的說明。
3.6.2 重定義已有方法
就像能給已有的類定義新方法一樣,也可重定義已有的方法。
如前一章所述,函數名只是指向函數的指標,因此可以輕易地使它指向其他函數。如果修改了本地方法,如 toString() ,會出現什麼情況呢?

Function.prototype.toString = function () {
return "Function code hidden";
}
前面代碼完全合法,運行結果完全符合預期:
function sayHi () {
alert("你好!");
}
alert(sayHi.toString()); //輸出"Function code hidden"
也許你還記得,第 2 章中介紹過 Function 的 toString() 方法通常輸出的是函數的原始碼。
覆蓋該方法,可以返回另一個字串(在這個例子中,返回 "Function code hidden " )。
不過, toString() 指向的原始函數怎樣了呢?它將被無用儲存單元回收程式回收,因為它被完全廢棄了。
沒能夠恢複原始函數的辦法,所以在覆蓋原始方法前,儲存它的指標比較安全,以便以後的使用。
你甚至可能在某種情況下在新方法中調用原始方法:
Function.prototype.originalToString = Function.prtotype.toString;
Function.prototype.toString = function () {
if(this.originalToString().length >100) {
return "Function too leng to display."
} else {
return this.originalToString();
}  
在這段代碼中,第一行代碼把對當前 toString() 方法的引

用儲存在屬性 originalTo- String 中。然後用定製的方法覆蓋了 toString() 方法。
新方法將檢查該函數原始碼的長度是否大於 100 。
如果是,就返回錯誤訊息,說明該函數代碼太長,否則調用 originalToString() 方法,返回函數的原始碼。

根據這個例子,只要照葫蘆畫瓢寫一行
Global.prototype.parseInt = Global.prototype.parseFloat;
那麼 parseInt函數真的就變成徒有其表,肚子裡面乾的卻是parseFloat勾當的函數了。
但是,同時一個致命的問題點也擺在眼前。
那就是JavaScript中的Global對象,就跟神一樣, 只是個概念。
說明請參見下面《JavaScript進階程式設計》70頁關於“內建對象”的說明。
Global對象是ECMAScript中最特別的對象,因為實際上它根本不存在。
如果嘗試編寫下面的代碼,將得到錯誤:
var pointer = Global;
錯誤訊息顯示Global不是對象,但剛才不是說Global是對象嗎?
沒錯。這裡需要理解的主要概念是,在ECMAScript中,不存在獨立的函數,所有函數都必須是某個對象的方法。
本書前面介紹的函數,如isNaN()、isFinite()、parseInt()和parseFloat()等,看起來都像獨立的函數。
實際上,它們都是Global對象的方法。

於是,上網大查怎樣擷取Global對象,或怎麼使用Global.prototype來改變parseInt函數。
結果可想而知,神就是神,就連著名的“搜神網”Google也查不出來。
欲放棄之時,果然應了那句“死地而後生”。突然想到parseInt就像個全域函數一樣,根本不用什麼對象調用。
那是不是說,只要把上面那句改成 parseInt = parseFloat;就可以了?
果然,神,無處不在!!! 好用了!!!

深度考究:
問題基本上解決了。只有一點需要注意的,就是JavaScript載入出錯的時候,後面的語句就不載入執行了。
所以這句一定要放在第一句執行。現在正好建一個JavaScript通用方法庫,要求以後每個頁面必須引入該庫檔案。
所以這一句放在該JavaScript通用方法庫的第一行,從此便可高枕無憂。
但是當我在為該段代碼寫注釋,特別是列舉如何應用時,發現如下代碼的問題
alert(parseInt("010", 2)); //10
alert(parseInt("010", 8)); //10
alert(parseInt("010", 10)); //10
每一個處理的傳回值都是10,也就是說可以處理二進位,八進位,十六進位的parseInt從此消失了。
如果說單個參數的parseInt惹出了不少麻煩,我們對於沒有惹禍的兩個參數的parseInt還是希望保留其特異功能的。
於是需要進一步的改進。
那麼就要根據使用parseInt函數時,傳遞參數的個數來進行判斷處理。
如果只有一個參數,那麼就調用parseFloat返回結果。
如果有兩個以上的參數,那麼就調用parseInt兩個參數的處理,返回結果。
這裡判斷參數個數用到arguments對象,參見《JavaScript進階程式設計》53~54頁關於arguments對象的說明。
在函數代碼中,使用特殊對象 arguments,開發人員無需明確指出參數名 ,就能訪問它們。
例如,在函數 sayHi() 中,第一個參數是 message。
用 arguments[0] 也可以訪問這個值,即第一個參數的值(第一個參數位於位置 0,第二個參數位於位置 1,依此類推)。
因此,無需明確具名引數,就可以重寫函數:
function sayHi() {
if (arguments[0] == "bye") {
return;
}
alert(arguments[0]);
}
於是就有了如下代碼:
originalparseInt = parseInt;
parseInt = function (){
if(arguments.length == 1){
return parseFloat(arguments[0]);
} else {
return originalparseInt(arguments[0], arguments[1]);
}
這段代碼裡我們改造了parseInt,讓其通過參數個數的不同進行不同的處理。
用一個新的變數originalparseInt保留了parseInt的原型。
這樣我們即便改造了parseInt,依然能通過保留的原型變數originalparseInt使用parseInt的原始功能。

返璞歸真:
代碼寫到這本以為一切都OK了,卻被人說徹底抹殺了parseInt函數對2進位,8進位的處理。
想想也是。處理的過於極端,只想著用parseFloat徹底替換掉討厭的parseInt函數。
如果我們正的用到2進位或8進位的數字轉換,還得用我們費勁保留的parseInt函數的原型新變數originalparseInt。
其實我們的願望很簡單。
parseInt函數中只有一個參數的時候就想讓它簡單的處理10進位的轉換,別再因為首位的0來產生一些頭疼的bug。
當我們用到第二個參數,想讓它處理2進位,8進位的時候,我還依然能用parseInt既存的功能。
於是有了下面最終的代碼:
考慮到js檔案體積的問題,盡量減少代碼量。於是把 originalparseInt 換成了 $parseInt。
另外把超級長的內建對象名arguments直接換成了一個字母 a 這樣該對象用了4次節省的代碼量就非常可觀了。

舉一反三:
對parseInt函數的再造就完成了。
那麼其實我們可以根據這次改造的經驗,改造與parseInt具有類似的煩人特性的JavaScript方法。
譬如,escape,unescape這種已經被 W3C組織不推薦使用的方法就可以用被推薦的方法替換掉

escape = encodeURI;
unescape = decodeURI;
那麼基於這次的經驗,今後遇到類似的問題就可以考慮到用這種乾坤大挪移的方法去解決了。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.