1.在ES5中函數(shù)定義有兩種方式:
函數(shù)聲明:?function functionName(arg){}
函數(shù)表達(dá)式:var functionName = function(arg){}
這兩種方式定義的函數(shù)都可以使用,但是他們還是有一些區(qū)別的
1.函數(shù)聲明方式定義的函數(shù),會(huì)有函數(shù)聲明提升的,即你可以在函數(shù)聲明之前調(diào)用改函數(shù),而函數(shù)表達(dá)式定義的函數(shù)不可以
2.匿名函數(shù),匿名函數(shù)顧名思義就是其函數(shù)沒(méi)有方法名字
function createCompareFunction(property){
return function (obj1,obj2){
var value1 = obj1[property];
var value2 = obj2[property];
if (value1 < value2){
return -1;
}else if (value1 > value2){
return 1;
}else{
return 0;
}
}
}
如上代碼所示,createCompareFunction就會(huì)返回一個(gè)匿名函數(shù)
3 es5中的遞歸函數(shù),可以使用arguments.callee(這個(gè)指向了當(dāng)前正在執(zhí)行的函數(shù))來(lái)實(shí)現(xiàn),避免函數(shù)表達(dá)式被賦值為其他,也可以使用命名函數(shù)表達(dá)式的方法
var factor = (function f (num){
if (num <=1){
return 1;
}else{
return num * f(num-1);
}
});
4.函數(shù)自執(zhí)行
(function (name){
console.log(name);
}('xindi'));
如下實(shí)現(xiàn),這個(gè)匿名函數(shù)會(huì)馬上執(zhí)行
5.閉包
閉包是指能有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù).
閉包在執(zhí)行的時(shí)候,會(huì)使用arguments和其他的命名參數(shù)創(chuàng)建自己的活動(dòng)對(duì)象,閉包的作用域鏈中會(huì)包含外部函數(shù)的活動(dòng)對(duì)象,當(dāng)在函數(shù)中訪問(wèn)一個(gè)變量時(shí),就會(huì)從其作用域鏈中搜索直到找到為止.所以這就是閉包為什么可以訪問(wèn)其外部函數(shù)的變量.
ps:this對(duì)象和arguments對(duì)象只會(huì)搜索自己的活動(dòng)對(duì)象,不會(huì)去訪問(wèn)外部函數(shù)的活動(dòng)對(duì)象.
但是閉包作用域鏈中保存的是整個(gè)的活動(dòng)對(duì)象,而不是某個(gè)特殊的變量,所以當(dāng)閉包執(zhí)行之后你可能得到的并不是你想要的結(jié)果,例如
function createFunctions (){
var newArr = new Array();
for (var i = 0;i<10;i++) {
newArr[i] = function (){
return i
}
}
return newArr;
}
createFunctions().map(function(item){
let result = item();
console.log(result);
});
這個(gè)例子中item就是閉包,每個(gè)item都都包含了createFunctions的活動(dòng)對(duì)象,但其實(shí)每個(gè)item引用的都是同一個(gè)活動(dòng)對(duì)象.當(dāng)createFunctions執(zhí)行完畢之后createFunctions內(nèi)部的變量I值為10,此時(shí)item(閉包)包含的活動(dòng)對(duì)象中I的值也是10.所以得到的結(jié)果會(huì)都是10;
當(dāng)然我們也可以采用其他的方式來(lái)避免這種情況,如果一定要使用閉包的話,可以采取下面的方式:
function createFunctions (){
var newArr = new Array();
for (var i = 0;i<10;i++) {
newArr[i] = function (num){
return function(){
return num;
}
}(i)
}
return newArr;
}
createFunctions().map(function(item){
let result = item();
console.log(result);
});
這個(gè)方式我們采取自執(zhí)行函數(shù),讓閉包返回的變量i的副本.
在閉包中我們還需要關(guān)注另一個(gè)問(wèn)題就是this對(duì)象和arguments對(duì)象.this和arguments對(duì)象只會(huì)在本身的活動(dòng)對(duì)象去尋找,所以當(dāng)有閉包的時(shí)候,閉包內(nèi)部訪問(wèn)的this和arguments對(duì)象可能不是你想要的.
我們可以有以下幾種解決方案:
1.將this和arguments對(duì)象用另一個(gè)變量保存下來(lái),在閉包內(nèi)部訪問(wèn)你保存的那個(gè)對(duì)象
2.借用bind函數(shù),創(chuàng)建一個(gè)this對(duì)象為你傳入的新函數(shù).
3.借用call 或 apply 改變閉包內(nèi)部的執(zhí)行環(huán)境
6.es5中模仿塊級(jí)作用域的變量,在es6中,實(shí)現(xiàn)let 和const兩個(gè)塊級(jí)作用域的變量,但是在es5中只有var,var聲明的作用域是非塊級(jí)的.不過(guò)我們可以使用閉包來(lái)模仿此行為
(function(){
var i = 0;
}())
如上所示,我們聲明了一個(gè)匿名的自執(zhí)行函數(shù),這樣在這個(gè)函數(shù)內(nèi)部的變量都會(huì)只在此函數(shù)內(nèi)部生效,類似塊級(jí)的變量.
7.私有變量,嚴(yán)格意義上來(lái)說(shuō)在js中并不存在私有變量,但是我們可以通過(guò)不同的方式來(lái)實(shí)現(xiàn)類似私有變量
1.特權(quán)方法:
function Person(){
var priviteVar = 10;
function privateFunc (){
console.log(priviteVar);
return 'lsh';
}
this.publicFunc = function(){
priviteVar += 1;
return privateFunc();
}
}
這樣實(shí)現(xiàn)的結(jié)果就是priviteVar 和privateFunc都只能通過(guò)publicFunc來(lái)訪問(wèn)
2.靜態(tài)私有變量:
(function(){
var priviteVar = 10;
function privateFunc(){
priviteVar += 1;
return 'lsh'+priviteVar;
}
Person = function (name){
this.name = name;
}
Person.prototype.publicFunc = function (){
return privateFunc();
}
}())
上面的實(shí)現(xiàn),構(gòu)造函數(shù)和priviteVar都處于同一個(gè)函數(shù)環(huán)境下,這樣priviteVar就會(huì)成為一個(gè)靜態(tài)變量,也就是你調(diào)用構(gòu)造函數(shù)創(chuàng)建多個(gè)對(duì)象,其仍然共享同一個(gè)priviteVar.
8.模塊模式
有時(shí)候程序中我們會(huì)需要一個(gè)單利對(duì)象來(lái)管理程序的一些信息,同時(shí)這個(gè)單利對(duì)象我們可能需要有它的私有變量,那么我們可以使用模塊模式來(lái)實(shí)現(xiàn)
var single = function (){
var privateVar = 10;
function privateFunc (){
return 'lsh';
}
var obj = new Object();
obj.publicProperty = false;
obj.publicFunc = function (){
privateVar += 1;
return privateFunc();
}
return obj;
}
這樣我們可以得到一個(gè)單利對(duì)象,同時(shí)這個(gè)對(duì)象有自己的私有方法和屬性,也有著公有方法和屬性;