不知道你們有沒有一種感受,那就是本身寫的javascript代碼雖然能徹底解決工做上的須要,可是,一眼望去,too simple!!!簡直就是一個傻子都能看懂的水平,因而,在工做之餘,我開始去收集一些幾乎在平常開發中不曾用過的javascript寫法/有價值的javascript知識點,藉此來提升自身javascript水平。javascript
1、函數式編程與面向對象編程html
<script> /*函數式編程*/ function f1(){ var n=999; nAdd=function(){ n+=1; }; var f2 = function(){ console.log(n); }; return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000 /*面向對象編程1之json寫法*/ var obj1 = { n:999, nAdd:function(){ this.n += 1; }, getN:function(){ console.log(this.n); } }; obj1.getN();//999 obj1.nAdd(); obj1.getN();//1000 /*面向對象編程2之工廠模式*/ var obj2 = function(){ var obj = new Object; obj.n = 999; obj.nAdd = function(){ this.n += 1; }; obj.getN = function(){ console.log(this.n); } return obj; }; var objins = new obj2(); objins.getN();//999 objins.nAdd(); objins.getN();//1000 /*面向對象編程3之原型prototype*/ function obj3(n) { this.n = n; } obj3.prototype.nAdd = function() { this.n+=1; }; obj3.prototype.getN = function() { console.log(this.n); } var objins2 = new obj3(999); objins2.getN();//999 objins2.nAdd(); objins2.getN();//1000 </script>
針對上面所示的函數式編程,涉及了做用域和閉包的知識,這裏簡要提一下;java
裏面的nAdd沒有用var關鍵字聲明,因此他是全局的,後面能夠全局調用,其次利用閉包訪問和修改了函數做用域內的n值。es6
2、純函數和非純函數編程
1.此函數在相同的輸入值時,老是產生相同的輸出。函數的輸出和當前運行環境的上下文狀態無關。json
2.此函數運行過程不影響運行環境,也就是無反作用(如觸發事件、發起http請求、打印/log等)。數組
簡單來講,也就是當一個函數的輸出不受外部環境影響,同時也不影響外部環境時,該函數就是純函數,也就是它只關注邏輯運算和數學運算,同一個輸入總獲得同一個輸出。緩存
var xs = [1,2,3,4,5]; // 純的 xs.slice(0,3); //=> [1,2,3] xs.slice(0,3); //=> [1,2,3] xs.slice(0,3); //=> [1,2,3] // 不純的 xs.splice(0,3); //=> [1,2,3] xs.splice(0,3); //=> [4,5] xs.splice(0,3); //=> []
純函數相對於非純函數來講,在可緩存性、可移植性、可測試性以及並行計算方面都有着巨大的優點。閉包
3、函數的柯里化ide
curry 的概念很簡單:將一個低階函數轉換爲高階函數的過程就叫柯里化。
函數柯里化是一種「預加載」函數的能力,經過傳遞一到兩個參數調用函數,就能獲得一個記住了這些參數的新函數。從某種意義上來說,這是一種對參數的緩存,是一種很是高效的編寫函數的方法:
/*通常寫法:兩數相加*/ var add = function(x,y){ return x + y; } /*2+1和2+10*/ add(2,1);//3 add(2,10);//12 /*柯里化寫法*/ //es5 var add = function(x) { return function(y) { return x + y; }; }; //es6 var add = x => (y => x + y); //定義+1和+10函數 var increment = add(1);//柯里化後獲得的+1函數 var addTen = add(10);//柯里化後獲得的+10函數 increment(2); // 3 addTen(2); // 12
4、Array.reduce/Array.reduceRight(數組迭代)
先不看標題,來個題目:如何求數組[1,2,3,4,5]的各項累加的和?
大多數人恐怕會當即說出for循環、while循環,的確,for循環和while循環是能夠解決上述問題,可是,這個答案顯然脫離了此篇博客的主題:逼格!!!
而且,實驗代表,將上述求和方法循環1W次,用for循環和while循環耗時大概在50~55ms,可是,用Array.reduce/Array.reduceRight僅僅耗時0.4ms!!!
關於該方法的詳解,請猛戳→https://www.w3cplus.com/javascript/array-part-8.html;這裏給個demo:
var arr = [1,2,3,4,5]; /*ES6寫法*/ var reducer = (prevalue,curvalue)=>prevalue + curvalue; console.log(arr.reduce(reducer));//15 /*ES5寫法*/ var reducer = function(prevalue,curvalue){ return prevalue + curvalue; }; console.log(arr.reduce(reducer));//15 /*解析*/ 數組的reduce方法有兩個參數:callback,prevalue; 對於第一個參數callback,裏面有4個參數prevalue、curvalue、index、array; index是當前迭代時的curvalue的索引(數組中的索引),array固然是操做的數組了; 着重看另外兩個參數,prevalue和curvalue(上一個值和當前值); 若是reduce方法沒有第二個參數的話,那麼callback回調將數組第一個值做爲prevalue,第二個值做爲curvalue; 若是reduce方法有第二個參數,那麼將第二個參數值做爲prevalue,將數組第一個值做爲curvalue; 每一次的操做結果將變爲下一次操做的prevalue,而curvalue的值教上一次日後推一位; /*實例解析*/ 針對上述數組相加; 沒有第二個參數,只有一個回調callback,那麼他的過程是這樣的; 第一次運算:prevalue爲1,curvalue爲2,運算結果爲1+2=3,並將結果做爲下次運算的prevalue; 第二次運算:prevalue爲3(上一次運算的結果),curvalue爲3,運算結果爲3+3=4,以此類推; 若是有第二個參數,假設爲5,arr.reduce(reducer,5),那麼將是這麼計算的; 第一次運算:prevalue爲5(第二個參數),curvalue爲1,運算結果爲5+1=6,並將結果做爲下次運算的prevalue; 第二次運算:prevalue爲6(上一次運算的結果),curvalue爲2,運算結果爲6+2=8,以此類推; reduceRight和reduce相似,只不過他是從右邊開始迭代的。
5、函數組合(reduce高級用法)
假想一下,要將字符串「my name is echo」進行以下操做,你會怎麼作?轉大寫、而後末尾加一個感嘆號;
我想你大概會定義一個函數operate,對於形參str,先toUpperCase(),而後+ '!',ok這樣作沒錯,可是如今我需求改了,我不要求轉大寫了,你是否是得刪除那行代碼?
可能你會說了,我定義兩個函數一個是轉大寫,一個是加歎號,須要哪一個用哪一個,其實不管你用幾個函數來寫,仍是直接用prototype或者鏈式調用的方法去寫,都只是大衆化的寫法,且不易維護,so,請看下面的寫法:
var compose = (...args) => x => args.reduceRight((prevalue, curvalue) => curvalue(prevalue), x); var toUpperCase = x => x.toUpperCase(); var exclaim = x => x + '!'; var shout = compose(exclaim, toUpperCase); console.log(shout("my name is echo"));
一眼望去,是否是兩眼懵逼?!!不怕,咱們用ES5還原一下,請看:
var compose = function (...args){ return function (x){ return args.reduceRight(function(prevalue, curvalue){ return curvalue(prevalue); },x) } }; var toUpperCase = function(x){ return x.toUpperCase(); }; var exclaim = function(x){ return x + '!'; }; var shout = compose(exclaim, toUpperCase); console.log(shout("my name is echo"));
結合上述第四例所講到的reduce,我但願此時你已經看懂了,沒看懂也不要緊,接着往下看,繼續解析:
var shout == compose(exclaim, toUpperCase) == function (x) { return [exclaim, toUpperCase].reduceRight(function(prevalue, curvalue){ return curvalue(prevalue); },x) }; shout("my name is echo") == [exclaim, toUpperCase].reduceRight(function(prevalue, curvalue){ return curvalue(prevalue); },"my name is echo"); /*前面說過,若是reduce/reduceRight傳了第二個參數,那麼該第二個參數將做爲prevalue給callback調用。*/ /*運算過程以下(reduceRight從右往左)curvalue(prevalue):*/ 1.toUpperCase("my name is echo");並將結果做爲prevalue供下次調用; 2.exclaim(toUpperCase("my name is echo"));如此實現了先轉大寫再加感嘆號。