系列更文前三篇文章,圍繞了一個重要的知識點:"函數"。
函數調用棧、函數執行上下文、函數作用域到閉包。可見不理解函數式編程,代碼都擼不好。
函數是一等公民
函數與其它數據類型一樣,可以作爲值賦給變量,作爲參數傳遞或返回值返回,也可以像對象一樣給函數創建屬性(不推薦給函數加屬性,雖然可用)。
函數在實際開發中應用:
- 函數聲明
- 函數表達式
- 匿名函數
- 自執行函數
// 函數聲明
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
作者:以樂之名
本文原創,有不當的地方歡迎指出。轉載請指明出處。