前端進擊的巨人(四):略知函數語言程式設計
系列更文前三篇文章,圍繞了一個重要的知識點: "函式" 。
函式呼叫棧、函式執行上下文、函式作用域到閉包。可見不理解函數語言程式設計,程式碼都擼不好。
函式是一等公民
函式與其它資料型別一樣,可以作為值賦給變數,作為引數傳遞或返回值返回,也可以像物件一樣給函式建立屬性(不推薦給函式加屬性,雖然可用)。
函式在實際開發中應用:
- 函式宣告
- 函式表示式
- 匿名函式
- 自執行函式
// 函式宣告 function getName() { //... } // 函式表示式 var getName = function() { //... } // 匿名函式 setTimeout(function(){ //... }, 1000); // 自執行函式 (function(){ //... })();
何為一等:優先順序
函式宣告在 "執行上下文建立階段" 就會進行宣告並賦值,而 var
宣告變數會初始化為 undefined
,實際賦值會等到 "執行上下文執行階段" 。函式表示式使用 var
來宣告,因此它遵循的是變數宣告的規則。 ( 如果函式名與變數重名,函式優先賦值)
"函式宣告優先順序高於變數宣告,函式表示式,自稱一等公民。"
// 程式碼書寫: console.log(getName); getName(); var getName; getName = '我的名字'; function getName(){ //... } console.log(getName); // 實際執行 var getName;// 變數名與函式名重名,函式優先賦值 function getName() { //... } console.log(getName); getName(); getName = '我的名字'; console.log(getName);
函數語言程式設計
函數語言程式設計是一種程式設計思維方式,它建議我們在程式編寫時,對複用性高的功能程式碼進行函式封裝,實現程式碼的高複用性。
新手朋友往往是一塊程式碼多次出現在不同的地方,常見的例子就是 ajax
請求方法運用,在需要請求後端資料時多次出現一串 ajax
請求程式碼。
如果想要對 ajax
請求統一做異常處理,或管理後端返回狀態碼,是不是每處程式碼都要修改???但是如果把 ajax
請求程式碼封裝成一個函式,介面 url
和資料 data
通過引數傳遞到函式內部處理,後期擴充套件維護都方便修改,複用性擴充套件性都更加優秀。
所以實際敲程式碼過程中,要經常提醒自己運用函數語言程式設計的思維方式,只要有可能出現多次的業務邏輯程式碼,那麼就要考慮是否封裝成函式,以便後續統一呼叫。
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()
...
純函式
純函式:相同的輸入對應相同的輸出,穩定沒有副作用(不改變外部變數的值)
相同的輸入,相同的輸出
相同的引數傳入呼叫,要有相同的結果輸出,概念有點繞,上程式碼栗子:
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物件的函式(純與不純)
// 不純的函式 array.push();// 陣列尾部插入 array.pop();// 刪除並返回陣列最後一個元素 array.unshift();// 陣列頭部插入 array.shift();// 刪除並返回陣列第一元素 array.splice();// 刪除元素,並向陣列新增元素 array.reverse();// 顛倒陣列元素的順序 array.sort();// 排序陣列元素 // 純函式 array.slice();// 陣列中返回選定的元素 array.concat();// 連線陣列,併發揮新陣列 array.join();// 按分隔符連線陣列,返回字串
純函式的應用:狀態管理Redux,Vuex
流行框架中狀態管理就是純函式的實踐應用,引用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作者:以樂之名