在設計javascript的繼承體系時,有一個重要需求,方法鏈。通俗地說,說是在方法中調用父類的同名方法。類似java的this.super().method()。如何把父類的同名方法封裝到子類中呢?這就要用到wrapper函數。之所以叫wrapper,而不是wrap,因為它比wrap更加wrapper。比如像Ext那種深度繼承的結構中,如果父類沒有找祖父,祖父沒有找曾祖父,沿著原型鏈層層上溯,以擷取它所需要的功能。此外,wrapper函數在jQuery也有應用,好像分為三種wrapAll,wrapinner,wrap,專門用來對付IE的table或其他DOM。可能還有其他用處,不管了,先看如何?它。
一個普通的函數
var greeting = function(world){ return "hello " + world +"!"; }; alert(greeting("world"));
<br /> var greeting = function(world){<br /> return "hello " + world +"!";<br /> };<br /> alert(greeting("world"));<br />
運行代碼
我們把它塞進更深一層的範圍,非bind函數。
var wrap= function(fn){ return function(){ return fn.apply(null,arguments); }; };
<br /> var wrap= function(fn){<br /> return function(){<br /> return fn.apply(null,arguments);<br /> };<br /> };<br /> var greeting = function(world){<br /> return "hello " + world +"!";<br /> };<br /> alert(greeting("world"));<br /> alert(wrap(greeting)("world"))</p><p>
運行代碼
但這隻是延遲了它的執行時間而已。上面函數中的null,也可以換成window。
var wrap= function(fn){ return function(){ return fn.apply(window,arguments); }; };
<br /> var wrap= function(fn){<br /> return function(){<br /> return fn.apply(window,arguments);<br /> };<br /> };<br /> var greeting = function(world){<br /> return "hello " + world +"!";<br /> };<br /> alert(greeting("world"));<br /> alert(wrap(greeting)("world"))</p><p>
運行代碼
因為總要人去調用函數的,null沒有此能力,就由window上。現在我們就是要在這個位置上做文章,把換成this。如果沒有進一步的改進,這裡的this還是window的替身。下面就開始複雜了,先分解一下寫法,降低閱讀難度,就像jQuery那樣把它掰成三部分:
var wrapper= function(fn){//這裡改一下名。 var temp = function(){ return fn.apply(this,arguments); }; return temp; };
//fn為原函數 //temp為改裝了的函數 //wrapper為封裝工廠,只運作一次 //wrap為改裝了的函數的屬性,它的參數和wrapper一樣為函數,但能運行無數次, //把原改裝了的函數內建為新增函數的內建函式。 var wrapper= function(fn){ var temp = function(){ return fn.apply(this,arguments); }; temp.wrap = function(callback){ var prev = fn; fn = function(){ return callback.apply(prev,arguments); }; }; return temp; };
這樣就可以如下神奇效果:
<br /> var wrapper= function(fn){<br /> var temp = function(){<br /> return fn.apply(this,arguments);<br /> };<br /> temp.wrap = function(callback){<br /> var prev = fn;<br /> fn = function(){<br /> return callback.apply(prev,arguments);<br /> };<br /> };<br /> return temp;<br /> };<br /> var html = function(str){ //原函數數<br /> return "<p>"+str+"</p><p>";<br /> }</p><p> alert(html("段落"));<br /> var p = wrapper(html);//第一次封裝<br /> alert(p("段落"))</p><p> p.wrap(function(str){//第二次封裝<br /> return ""+this(str)+"<p>";<br /> });</p><p> alert(p("段落"));</p><p> p.wrap(function(str){//第三次封裝<br /> return ""+this(str)+"";<br /> });<br /> alert(p("段落"));<br />
運行代碼
可以看出,this總是為原來的同名函數(p),也就是說,我們可以稱之為父方法,這樣也super關鍵字或者相關的代替品也不用了,輕鬆調用原來覆蓋了父類的方法。因此jQuery同學單是用它來處理DOM真是大材小用。Prototype同學就做得不錯了,不過代碼寫得比較艱澀:
wrap: function(wrapper) { var __method = this; return function() { return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); } },
它的方法鏈設計:
for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; if (ancestor && Object.isFunction(value) && value.argumentNames().first() == "$super") { var method = value; value = (function(m) { return function() { return ancestor[m].apply(this, arguments) }; })(property).wrap(method); value.valueOf = method.valueOf.bind(method); value.toString = method.toString.bind(method); } this.prototype[property] = value; }
相關文檔:http://www.prototypejs.org/learn/class-inheritance