函數柯里化,又稱部分求值,一個currying 的函數首先會接受一些參數,接受了這些參數以後,該函數並不會當即求值,而是繼續返回另一個函數,剛纔傳入的參數在函數造成的閉包中被保存起來。待到函數被真正須要求值的時候,以前傳入的全部參數都會被一次性用於求值。javascript
var cost = (function(){ var args = []; return function(){ if ( arguments.length === 0 ){ var money = 0; for ( var i = 0, l = args.length; i < l; i++ ){ money += args[ i ]; } return money; }else{ [].push.apply( args, arguments ); } } })(); cost( 100 ); // 未真正求值 cost( 200 ); // 未真正求值 cost( 300 ); // 未真正求值 console.log( cost() ); // 求值並輸出:600
帶參數不求值 不帶參數求值java
var currying = function( fn ){ var args = []; return function(){ if ( arguments.length === 0 ){ return fn.apply( this, args ); }else{ [].push.apply( args, arguments ); //arguments.callee 在哪個函數中運行,它就表明哪一個函數,主要用在匿名函數中 return arguments.callee; } } }; var cost = (function(){ var money = 0; return function(){ for ( var i = 0, l = arguments.length; i < l; i++ ){ money += arguments[ i ]; } return money; } })(); var cost = currying( cost ); // 轉化成currying 函數 cost函數有對參數的集中處理 cost( 100 ); // 未真正求值 cost( 200 ); // 未真正求值 cost( 300 ); // 未真正求值 alert ( cost() ); // 求值並輸出:600
第二個例子更爲通用,把柯里化的過程抽離了出來。設計模式
柯里化的過程是逐步傳參,逐步縮小函數的適用範圍,逐步求解的過程。而反柯里化的做用在與擴大函數的適用性,使原本做爲特定對象所擁有的功能的函數能夠被任意對象所用。數組
obj.func(arg1, arg2) =====> func(obj, arg1, arg2)瀏覽器
動態語言比較容易實現這一點,下面是實現方式之一:閉包
var uncurrying= function (fn) { return function () { var args=[].slice.call(arguments,1); return fn.apply(arguments[0],args); } }; var test="a,b,c"; var split_new=uncurrying( String.prototype.split ); test.split(","); //["a","b","c"] split_new(test,','); //["a","b","c"]
減小沒必要要的函數自主調用,好比window.onresize事件 mousemove的拖曳事件等app
實現方法之一:函數
將即將被執行的函數用setTimeout延遲一段時間執行。若是該次延遲執行尚未完成,則忽略接下來調用該函數的請求。函數接受2個參數,第一個參數爲須要被延遲執行的函數,第二個參數爲延遲執行的時間。性能
var throttle = function ( fn, interval ) { var __self = fn, // 保存須要被延遲執行的函數引用 timer, // 定時器 firstTime = true; // 是不是第一次調用 return function () { var args = arguments, __me = this; if ( firstTime ) { // 若是是第一次調用,不需延遲執行 __self.apply(__me, args); return firstTime = false; } if ( timer ) { // 若是定時器還在,說明前一次延遲執行尚未完成 return false; } timer = setTimeout(function () { // 延遲一段時間執行 clearTimeout(timer); timer = null; __self.apply(__me, args); }, interval || 500 ); }; }; window.onresize = throttle(function(){ console.log( 1 ); }, 500 );
數據過大時,好比好友列表中有上千的好友須要渲染,瀏覽器只會一口氣所有去渲染出來,這頗有可能佔據大量的性能,形成嚴重卡頓。這裏能夠人爲控制,將1000ms渲染1000個變成200ms渲染8個,分批渲染,減少壓力this
var timeChunk = function( ary, fn, count ){ var obj, t; var len = ary.length; var start = function(){ for ( var i = 0; i < Math.min( count || 1, ary.length ); i++ ){ //肯定執行的數量,而後遍歷執行 var obj = ary.shift(); //取出第一個元素 fn( obj ); //執行函數 } }; return function(){ t = setInterval(function(){ if ( ary.length === 0 ){ // 若是數組長度爲0,表示結束 return clearInterval( t ); } start(); }, 200 ); // 分批執行的時間間隔,也能夠用參數的形式傳入 }; }; var ary = []; for ( var i = 1; i <= 1000; i++ ){ ary.push( i ); }; //這裏是隨便給一些數據,數據一共1000個 var renderFriendList = timeChunk( ary, function( n ){ var div = document.createElement( 'div' ); div.innerHTML = n; document.body.appendChild( div ); }, 8 ); renderFriendList();
在Web 開發中,由於瀏覽器之間的實現差別,一些嗅探工做老是不可避免。好比咱們須要一個在各個瀏覽器中可以通用的事件綁定函數addEvent,常見的寫法以下:
var addEvent = function( elem, type, handler ){ if ( window.addEventListener ){ return elem.addEventListener( type, handler, false ); } if ( window.attachEvent ){ return elem.attachEvent( 'on' + type, handler ); } };
這個函數的缺點是,當它每次被調用的時候都會執行裏面的if 條件分支,形成沒必要要的性能浪費。
第二種方案以下,把它提早到到代碼加載的時候,自執行一次,只調用一次判斷,把內部的邏輯預先肯定好。
var addEvent = (function(){ if ( window.addEventListener ){ return function( elem, type, handler ){ elem.addEventListener( type, handler, false ); } } if ( window.attachEvent ){ return function( elem, type, handler ){ elem.attachEvent( 'on' + type, handler ); } } })();
這種方案也有個問題,也許咱們從頭至尾都不會使用addEvent函數,這一步就是個多餘操做。
第三種方案就是惰性載入函數方案,addEvent依然被聲明爲一個普通函數,在函數裏依然有一些分支判斷。可是在第一次進入條件分支以後,在函數內部會重寫這個函數,重寫以後的函數就是咱們指望的addEvent函數,在下一次進入addEvent函數的時候,addEvent函數裏再也不存在條件分支語句:
1 var addEvent = function( elem, type, handler ){ 2 if ( window.addEventListener ){ 3 addEvent = function( elem, type, handler ){ //和前面方案的區別在於這裏重寫了本身 4 elem.addEventListener( type, handler, false ); 5 } 6 }else if ( window.attachEvent ){ 7 addEvent = function( elem, type, handler ){ 8 elem.attachEvent( 'on' + type, handler ); 9 } 10 } 11 addEvent( elem, type, handler ); //這裏只會第一次執行一次,後面都是直接執行重寫後的 12 }; 13 14 15 var div = document.getElementById( 'div1' ); 16 17 addEvent( div, 'click', function(){ 18 alert (1); 19 }); 20 addEvent( div, 'click', function(){ 21 alert (2); 22 });
由於javascript自身的特色,閉包和高階函數應用極多,在JavaScript中,不少設計模式都是經過閉包和高階函數實現的。很重要的一點是,相對於模式的實現過程,咱們更關注的是模式能夠幫助咱們完成什麼。