首先看一段代碼: let obj = { x: 100 }; function fn(y) { this.x += y; console.log(this); } 現在有一個需求:在1秒後,執行函數fn,並讓其this指向obj。 如果寫成 setTimeout(fn, 1000); 這麼寫的話,f ...
首先看一段代碼:
let obj = { x: 100 }; function fn(y) { this.x += y; console.log(this); }
現在有一個需求:在1秒後,執行函數fn,並讓其this指向obj。
如果寫成
setTimeout(fn, 1000);
這麼寫的話,fn函數中的this是指向window的,而且也沒有傳遞參數。
如果寫成
setTimeout(fn(200), 1000);
這麼寫的話,this指向依然是window,而且相當於立即執行fn函數,並把結果賦給定時器1秒後再執行,這樣肯定不行。
如果寫成
setTimeout(fn.call(obj,200), 1000);
這麼寫的話,用call或apply雖然改變了this指向,但都是函數立即執行並把返回結果賦給了定時器,依然無法完成需求。
如果寫成
setTimeout(function() { fn.call(obj, 200); }, 1000);
這麼寫的話,用一個匿名函數包起來,等到1秒後執行匿名函數裡邊的代碼,這樣便可以完成上述需求。
從中我們可以看出,在某一個階段之後執行某些代碼,我們需要預先把this指向、參數等預先準備好,這種預先處理的思想即柯理化思想。
即,柯理化函數的思想:利用閉包的機制,把一些內容事先存儲和處理了,等到後期需要的時候拿來用即可。當然,這個需求如果用bind寫的話,也能實現
setTimeout(fn.bind(obj,200), 1000);因為bind不會立即執行函數,而且可以預先存儲一些內容,和柯理化函數的思想相似,但問題是bind不相容IE8及以下,那麼為了通用,我們可以自己封裝一個bind方法,從而實現這種需求。
/* * bind:預先處理內容 * @params * func:要執行的函數 * context:需要改變的this指向 * args:給函數傳遞的參數 * @return * 返回一個代理函數 */ function bind(func, context, ...args) { return function proxy() { func.call(context, ...args); //call和apply相容低版本IE }; }
完成最開始的需求
setTimeout(bind(fn, obj, 200), 1000);
理解了這種思想,有助於我們更好的閱讀別人的代碼,如redux源碼中很多都用到了這種方式或思想,以後更有助於我們自己寫一些插件、組件等。