系列更文前三篇文章,圍繞了一個重要的知識點:"函數"。
函數調用棧、函數執行上下文、函數做用域到閉包。可見不理解函數式編程,代碼都擼很差。javascript
函數與其它數據類型同樣,能夠做爲值賦給變量,做爲參數傳遞或返回值返回,也能夠像對象同樣給函數建立屬性(不推薦給函數加屬性,雖然可用)。html
// 函數聲明 function getName() { //... } // 函數表達式 var getName = function() { //... } // 匿名函數 setTimeout(function(){ //... }, 1000); // 自執行函數 (function(){ //... })();
函數聲明在"執行上下文建立階段"就會進行聲明並賦值,而var
聲明變量會初始化爲undefined
,實際賦值會等到"執行上下文執行階段"。函數表達式使用var
來聲明,所以它遵循的是變量聲明的規則。( 若是函數名與變量重名,函數優先賦值)前端
"函數聲明優先級高於變量聲明和函數表達式,自稱一等公民。"java
// 代碼書寫: console.log(getName); getName(); var getName; getName = '個人名字'; function getName(){ //... } console.log(getName); // 實際執行 var getName; // 變量名與函數名重名,函數優先賦值 function getName() { //... } console.log(getName); getName(); getName = '個人名字'; console.log(getName);
函數式編程是一種編程思惟方式,它建議咱們在程序編寫時,對複用性高的功能代碼進行函數封裝,實現代碼的高複用性。git
新手朋友每每是一塊代碼屢次出如今不一樣的地方,常見的例子就是ajax
請求方法運用,在須要請求後端數據時屢次出現一串ajax
請求代碼。程序員
若是想要對ajax
請求統一作異常處理,或管理後端返回狀態碼,是否是每處代碼都要修改???可是若是把ajax
請求代碼封裝成一個函數,接口url
和數據data
經過參數傳遞到函數內部處理,後期擴展維護都方便修改,複用性擴展性都更加優秀。github
因此實際敲代碼過程當中,要常常提醒本身運用函數式編程的思惟方式,只要有可能出現屢次的業務邏輯代碼,那麼就要考慮是否封裝成函數,以便後續統一調用。ajax
function sumScore(list) { var totalScore = 0 for (var i = 0; i < list.length; i++) { totalScore += list[i]; } return totalScore; } var list = [10, 8, 9, 7]; var totalScore = sumScore(list); // 計算總分
TIPS: 函數名建議使用動詞,如addUser(),sumScore(),getUser()
...編程
純函數:相同的輸入對應相同的輸出,穩定沒有反作用(不改變外部變量的值)
相同的參數傳入調用,要有相同的結果輸出,概念有點繞,上代碼栗子:redux
function getDate() { return new Date(); } var dateOne = getDate(); var dateTwo = getDate(); var dateThr = getDate();
上述代碼中調用了三次getDate()
,三次返回的值都不同。相同的輸入並無相同的輸出,因此getDate()
並非一個純函數。
TIPS:函數中使用new Date()
,Math.random()
, 異步等均可能形成函數不穩定。
部分小夥伴的代碼,在函數裏面直接修改參數的值,這是一種很是不推薦的作法,這樣作會形成代碼環境不可控制,污染外部變量環境,一旦出現錯誤排查起來:心累,三個字心好累。
函數有本身的局部做用域,所以函數中,對須要使用到的變量,管控在自身的做用域下。若是須要修改外部參數的值,經過函數返回值返回給函數調用者。修改外部參數值的操做不在函數內進行,確保對外部環境沒有反作用。
TIPS:參數爲引用類型時,參數複製的是地址指針,避免修改了引用類型中屬性值污染外部環境,如需使用建議手動深拷貝賦值。
function getGirlGift(list) { // 避免污染參數爲引用類型的list,對list深拷貝 var newList = JSON.parse(JSON.stringify(list)); newList.map(girl => { girl.gift = girl.age > 18 ? 'lipstick' : 'chocolates'; }); return newList; // 返回新值 } var girlList = [ {name: 'Kelly', age: 20}, {name: 'Alic', age: 16}, {name: 'Moon', age: 23}, {name: 'Nana', age: 17} ]; var girlGiftList = getGirlGift(girlList); girlList // 原用girlList不變 girlGiftList // 每一個girl多了gift屬性
// 不純的函數 array.push(); // 數組尾部插入 array.pop(); // 刪除並返回數組最後一個元素 array.unshift(); // 數組頭部插入 array.shift(); // 刪除並返回數組第一元素 array.splice(); // 刪除元素,並向數組添加元素 array.reverse(); // 顛倒數組元素的順序 array.sort(); // 排序數組元素 // 純函數 array.slice(); // 數組中返回選定的元素 array.concat(); // 鏈接數組,併發揮新數組 array.join(); // 按分隔符鏈接數組,返回字符串
流行框架中狀態管理就是純函數的實踐應用,引用redux的應用,reducer
中返回新的狀態數據state
,但不能去直接去修改state
數據,如下爲redux中reducer
的例子代碼:
export default (state = defaultState, action) => { let newState = JSON.parse(JSON.stringify(state)); switch (action.type) { case DELETE_TODO_ITEM: newState.list.splice(action.value, 1); break; case ADD_TODO_ITEM: if (newState.inputValue.trim().length) { newState.list.push(newState.inputValue); } newState.inputValue = ''; break; case INIT_LIST_ACTION: newState = action.data break; default: break; } return newState; }
上篇中《前端進擊的巨人(三):從做用域走進閉包》咱們講解了做用域、閉包的原理機制。
"自執行函數可實現塊級做用域,而閉包則可實現外部環境對函數做用域內部數據的訪問。"
// 自執行函數 + 閉包實現模塊化 (function MakeModule(window) { var name = '以樂之名'; var age = 28; var job = '程序員'; function changeJob(newJob) { job = newJob; } function getName() { return name; } window.modulePublic = { changeJob: changeJob, getName: getName } })(window); window.modulePublic.getName(); window.modulePublic.changeJob('產品經理');
對做用域,以及閉包知識還沒掌握的小夥伴,可回閱《前端進擊的巨人(三):從做用域走進閉包》。
高階函數是一個函數,它接收函數做爲參數或將函數做爲輸出返回
JavaScript中經常使用的高階函數:
Array.prototype.map
(映射遍歷)Array.prototype.filter
(過濾)Array.prototype.reducer
(累計)除了內置的高階函數,咱們實際開發中,高階函數應用的最多就是回調函數了。
function getOrder(url, datas, callBack) { return $.post(url, datas, callBack(orderInfo)); } // getOrder就是一個高階函數,接收callBack函數做爲參數
高階函數的概念很簡單,"自己是函數,參數是函數,或返回值是函數"。
參考文檔:
系列更文請關注專欄:《前端進擊的巨人》,不斷更新中。。。
本文首發Github,期待Star!
https://github.com/ZengLingYong/blog
做者:以樂之名 本文原創,有不當的地方歡迎指出。轉載請指明出處。