閱讀作者原文
初階部分
字符串可以保存為變量,函數(shù)說(shuō)他也可以
var autumn = 'autumnswind';
var autumn_fn = function() {
return 'autumnswind';
};
字符串可以保存對(duì)象字段耐朴,函數(shù)說(shuō)他也可以
var autumnswind = {
autumn: 'autumnswind',
autumn_fn: function() {
return 'autumnswind'
}
};
字符串可以用時(shí)再創(chuàng)建手销,函數(shù)說(shuō)他也可以
'Autumns' + (function() {
return 'Wind'
})();
字符串可以作為參數(shù)傳給函數(shù),函數(shù)說(shuō)他也可以
var autumn;
function autumn_fn(){};
function hellloWorld(autumn, autumn_fn) {
return;
}
字符串可以作為函數(shù)返回值碧信,函數(shù)說(shuō)他也可以
return 'autumnswind';
return function() {
return 'autumnswind';
};
所以函數(shù)真的是JS里面的上等好公民啊可以穿梭于任何地方,并談笑風(fēng)生
高階部分:
有了上面的例子输涕,那么我們就可以把函數(shù)這個(gè)小孩子帶到好多好玩的地方,往下慢慢看~
首先看第一個(gè)例子
var
obj1 = {
value: '秋風(fēng)'
},
obj2 = {
value: 'autumnswind'
},
obj3 = {
value: '莎娜'
};
var values = [];
function add(obj) {
values.push(obj.value);
}
add(obj1);
add(obj2);
console.log(values); // 秋風(fēng),autumnswind
這種寫法慨畸,大家都知道了莱坎,變量會(huì)污染全局環(huán)境,并且一旦有一個(gè)地方忘記修改了就會(huì)變得很不穩(wěn)定寸士,不像函數(shù)體一樣檐什,只負(fù)責(zé)內(nèi)部的輸入輸出碴卧,這里如果融入上下文的其他函數(shù)和變量就會(huì)變得非常混亂
根據(jù)這樣修改成第二個(gè)例子:
var
obj1 = {
value: '秋風(fēng)'
},
obj2 = {
value: 'autumnswind'
},
obj3 = {
value: '莎娜'
};
function add(obj) {
var values = [];
values.push(obj.value);
return values;
}
跟下面一樣
/*function add(obj) {
var values = [];
var accumulate = function() {
values.push(obj.value);
};
accumulate();
return values;
}*/
add(obj1);
add(obj2);
console.log(add(obj1)); // 秋風(fēng)
console.log(add(obj2)); // autumnswind
這樣我們把values數(shù)組聲明放進(jìn)去函數(shù)內(nèi)部乃正,這樣就不會(huì)被其他外部變量騷擾了住册,但是問(wèn)題又來(lái)了,只有最后傳入對(duì)象值才可以返回瓮具,那不是我們的初衷
最后看消除所有尷尬的第三個(gè)例子:
var
obj1 = {
value: '秋風(fēng)'
},
obj2 = {
value: 'autumnswind'
},
obj3 = {
value: '莎娜'
};
var
Add = function(obj) {
var
values = [];
var
_calc = function(obj) {
if (obj) {
values.push(obj.value);
return values;
} else {
return values;
}
};
return _calc;
//可以這樣把它看成匿名函數(shù)省去一個(gè)沒(méi)必要的變量
/*return function(obj) {
if (obj) {
values.push(obj.value);
return values;
} else {
return values;
}
}*/
};
var
calc = Add();
calc(obj1); //相當(dāng)于ValueAccumulator()(obj1)
calc(obj2); //走if分支
console.log(calc()); //走else的分支
這里制造一個(gè)閉包來(lái)保存第一層函數(shù)的values數(shù)組荧飞,這個(gè)數(shù)組既可以被內(nèi)部函數(shù)_calc調(diào)用,也可以在自己函數(shù)體內(nèi)調(diào)用名党;第一層函數(shù)的關(guān)鍵點(diǎn)在于返回的是在自己內(nèi)部定義的那個(gè)函數(shù)_calc叹阔,這個(gè)hack就能讓Add函數(shù)在外部能訪問(wèn)函數(shù)體內(nèi)的一直保存的values數(shù)組,進(jìn)一步說(shuō)這種方法可以得到第一層函數(shù)任何一個(gè)聲明的變量传睹,這是閉包的一個(gè)很常見(jiàn)的用法(返回函數(shù))耳幢。
理解下面這些話
JS中的閉包就是函數(shù)可以訪問(wèn)父作用域(這個(gè)總結(jié)夠短的,有爭(zhēng)議的可以先擱置爭(zhēng)議)
上面其實(shí)可以看做是自執(zhí)行的匿名函數(shù)(_calc可以它看成一個(gè)匿名函數(shù)輸出來(lái))欧啤,自執(zhí)行的函數(shù)實(shí)際上是高階函數(shù)的一種形式睛藻。高階函數(shù)就是以其它函數(shù)為輸入,或者返回一個(gè)函數(shù)為輸出的函數(shù)(這個(gè)理解越來(lái)越像是閉包的理解了)
所以函數(shù)將其他函數(shù)作為輸入?yún)?shù)或者作為返回值邢隧,就可以稱為高階函數(shù)
上面其實(shí)還需要了解一個(gè)知識(shí)點(diǎn)就是純函數(shù)
//非純函數(shù)
var autumn1 = function(str) {
window.innerHeight;
window.innerWidth;
return str + 's innerHeight: ' + window.innerWidth + 'px';
};
//純函數(shù)
var autumn2 = function(str, height) {
return str + 's innerHeight: ' + height + 'px';
};
console.log(autumn1('autumnswind'));
console.log(autumn2('autumnswind', 254));
兩個(gè)函數(shù)的區(qū)別在于非純函數(shù)依賴window這個(gè)全局對(duì)象的狀態(tài)來(lái)計(jì)算寬度和高度店印,而自給自足的純函數(shù)則要求這些值作為參數(shù)傳入,當(dāng)一個(gè)函數(shù)是純的府框,也就是不依賴于當(dāng)前狀態(tài)和環(huán)境吱窝,我們就不用管它實(shí)際的計(jì)算結(jié)果是什么時(shí)候被計(jì)算出來(lái)。
純函數(shù)返回的計(jì)算結(jié)果僅與傳入的參數(shù)相關(guān)
純函數(shù)是完完全全獨(dú)立的迫靖,所以它適合被重復(fù)調(diào)用使用院峡,為下面的函數(shù)編程做一個(gè)好鋪墊
有更深刻理解或者有誤的話請(qǐng)務(wù)必提醒并留言我~
回到題目核心的JS函數(shù)編程
由于JS中,函數(shù)是頭等公民系宜,這里的實(shí)際意思就是函數(shù)被認(rèn)定最基本最根本的類型照激,正如數(shù)字和對(duì)象一樣。 如果數(shù)字和對(duì)象可以被來(lái)回傳遞盹牧,那么函數(shù)(函數(shù)我為什么就不可以呢)也可以的俩垃。
這就是JS函數(shù)編程的基本思想吧
那下面第四段代碼就是在第三段代碼后面的基礎(chǔ)上再添加,實(shí)現(xiàn)這種思路
var calc2 = Add();
var objects = [obj1, obj2, obj3]; // 這個(gè)數(shù)組可以很大
objects.forEach(calc2);
//注意對(duì)象foeEach的用法 array.forEach(callbackfn[, thisArg])汰寓;對(duì)于數(shù)組中的每個(gè)元素口柳,forEach都會(huì)調(diào)用callbackfn函數(shù)一次
console.log(calc2());
這里的calc2函數(shù)就變成一個(gè)‘變量’進(jìn)入到forEach參數(shù)內(nèi)
我們可以在這個(gè)基礎(chǔ)上繼續(xù)擴(kuò)充功能,第五段代碼有滑,鏈?zhǔn)秸{(diào)用
objects1 = objects.reverse();
objects2 = objects1.concat([4, 16]);
objects3 = objects2.map(Math.sqrt);
console.log(objects3);
用jQuery用多了跃闹,就會(huì)知道,jq習(xí)慣把類似上面的代碼鏈?zhǔn)秸{(diào)用
類似這樣把代碼串在一起
console.log(['秋風(fēng)', 'autumnswind', '莎娜'].reverse().concat([4, 16]).map(Math.sqrt)); //NaN,NaN,NaN,2,4
//寫明顯一點(diǎn)
/*console.log(
['秋風(fēng)', 'autumnswind', '莎娜']
.reverse()
.concat([4, 16])
.map(Math.sqrt)
);*/
這樣用的前提必須是這個(gè)函數(shù)對(duì)象里面擁有這個(gè)方法,換句話說(shuō)就是Array.prototype有這個(gè)方法(例如reverse, concat ,map)
給他擴(kuò)充一個(gè)簡(jiǎn)單的方法如下:
Array.prototype.make4 = function() {
console.log(this[4]);
//this.length=4;
return this[4];
}
console.log(['秋風(fēng)', 'autumnswind', '莎娜'].reverse().concat([4, 16]).map(Math.sqrt).make4()); //4
上面除了展示匿名函數(shù)望艺,鏈?zhǔn)秸{(diào)用苛秕,還有有一種方法是函數(shù)式編程里面常用的,就是遞歸
var Add2 = function(n) {
if (n < 0) {
// 基準(zhǔn)情形
return 'I am autumnswind';
} else {
// 遞歸情形
return Add2(n - 1);
}
}
console.log(Add2(5));
注意找默,基準(zhǔn)情形就是讓遞歸停止下來(lái)的條件艇劫,防止無(wú)限遞歸
再?gòu)?fù)雜點(diǎn)就叫分而治之,其實(shí)數(shù)學(xué)界很多問(wèn)題都可以用遞歸解決的
var Add3 = function(a, b) {
if (a < 0 && b == 8) {
console.log(a % b); //-1
// 基準(zhǔn)情形
return 'I am autumnswind';
} else {
// 遞歸情形
return Add3(a - 1, b);
}
}
console.log(Add3(5, 8));
console.log(Add3(5, 7)); //Maximum call stack size exceeded