JS方法代理,js代理
作者:Jiang, Jilin
JS作為一門指令碼語言,十分容易上手。外加其靈活性,可以輕而易舉地擴充功能。今天,我們就聊聊JS的方法代理。方法代理是指令碼語言中常見的方法擴充形式。這種靈活的形式優點在於遇到複雜的JS代碼需要擴充時,可以相對簡單的抽取並修改。但是,其缺點也十分明顯,會造成代碼的片段化,因而是一把雙刃劍。
基本形式
var _func = obj.func; obj.func = function() { return _func(); }; |
此處,obj的func方法是原始方法。我們使用一個_func變數儲存該方法,之後重寫func方法,返回調用原始的方法。相當的好理解。
不定長參數
有時候,我們會遇到不定長參數的形式。那麼我們可以做以下更改:
var _func = obj.func; obj.func = function() { return _func.apply(this, arguments); }; |
寫到這裡,JS方法代理的兩種形式已經寫完了。這時,你可能會疑惑這種方法代理有什麼用?那麼,就跟隨我看看幾個例子吧:
重寫靜態方法
出於某些原因,我們需要在console列印的同時,將列印資料轉送到指定伺服器上使用者協助統計資料。而在開發過程中,直接使用了原生態的console.log方法輸出。那麼,我們就可做如下修改。
首先,構建代理方法:
var _log = console.log; console.log = function() { return _log.apply(this, arguments); }; |
接著,插入ajax調用邏輯:
console.log = function() { var _args = Array.prototype.slice.call(arguments); $.post("srvAddr", {userID: userID, args: _args}); return _log.apply(this, arguments); }; |
可以很容易預見效果:
原型代理
有時候,我們需要直接接管一個類的方法。那麼相應的,我們就要將其prototype中的方法做代理。現在我們假設要使得Array預設支援數字排序:
var _sort = Array.prototype.sort; Array.prototype.sort = function() { _sort.apply(this, arguments); }; |
我們首先檢測數組中的元素是否是數字,如果是則按照數字排序,否則就按照預設排序:
var _sort = Array.prototype.sort; Array.prototype.sort = function() { var _isNum = true; for(var i = 0 ; i < this.length ; i += 1) { if(typeof this[i] !== "number") { _isNum = false; break; } } if(_isNum) { return _sort.call(this, function(a, b) { return a - b; }); } else { return _sort.apply(this, arguments); } }; |
原始排序方式(將數字轉換成字元排序)
代理後排序方式
注意:此處僅作為原型方法代理的例子。在實際開發中,應該盡量避免對原生對象原型方法做代理。
重載
在jQuery中,toggle方法擁有多種重載,其中一個為toggle(display),通過接受boolean參數來決定元素顯示還是隱藏。而fadeToggle方法則不接受該參數。讓我們進行簡單的代理,使其支援boolean參數:
var _fadeToggle = $.fn.fadeToggle; $.fn.fadeToggle = function(display) { if(arguments.length === 1 && typeof display === "boolean") { if(display) { return this.fadeIn(); } else { return this.fadeOut(); } } return _fadeToggle.apply(this, arguments); }; |
通過參數長度和接收參數類型類型判斷是否調用fadeIn或fadeOut方法。如果不匹配則調用原來的fadeToggle方法。以此類推,slideToggle也可以同樣適用。
*繼承
繼承中,我們有時候會需要調用父類方法來實現。這和java中的super有些類似。一個簡單的例子。我們定義了一個Person,可以laugh。同時定義一個Robot繼承,是其沿用Person的laugh但是稍作修改:
var Person = function() { this.name = ""; this.laugh = function() { return "Ha ha!"; }; return this; }; var Robot = function() { Person.apply(this); var _laugh = this.laugh; this.laugh = function() { return encodeURIComponent(_laugh()); }; return this; }; |
劫持
說到劫持,則和安全性開始掛鈎了。如今大多數網站都習慣於使用開源的js庫來開發,但是如果碰巧被插入了惡意代碼,那麼安全也就變得不安全了(例如chrome中大量未經驗證的遊覽器外掛程式)。以下,我們將舉幾個劫持的例子。
Ajax劫持
不少人以為,通過閉包的方式將重要使用者參數儲存在範圍內(例如動態產生的secureID),那麼即便是被插入了惡意代碼也無法擷取到,從而無法偽造ajax請求來擷取重要訊息似乎就足夠了。但是如果整個ajax請求被攔截了呢?
var _ajax = $.ajax; $.ajax = function() { var $p = _ajax.apply($, arguments); $p.done(function(data) { // Do something... }); return $p; }; |
jQuery中所有ajax請求(get,post,getJSON)最終都會調用$.ajax來實現。而通過方法代理直接劫持$.ajax方法並返回一個正常promise對象,會使得頁面指令碼運行毫無影響。但是實際上資料已經被劫持走了。
Array劫持
Array擁有不少原型方法,例如push,pop,shift,unshift等等。我們只需要簡單的代理數組操作方法,便可以截獲資料。
var _push = Array.prototype.push; Array.prototype.push = function() { // Do something... return _push.apply(this, arguments); }; |
好了,以上就是這次的方法代理介紹。由於方法代理會混亂代碼邏輯使得代碼結構變得不易理解。在日常開發過程中,我們應該盡量避免使用它。只有在代理重載,或者更改難以理解的遺留代碼的部分邏輯時使用它。從而避免將雙刃劍誤傷了自己。