JavaScript函數表達式——「函數的遞歸和閉包」的注意要點css
name
屬性和函數提高首先,name
屬性,經過這個屬性能夠訪問到給函數指定的名字。(非標準的屬性)如:html
function People(){}; console.log(People.name); //People
其次,函數聲明提高,意味着能夠把函數聲明放在調用它的語句後面。如:前端
sayHi(); //調用函數 function sayHi(){ //聲明函數 console.log("Hi"); } //不會報錯
使用函數表達式則不能夠:vue
sayHi(); var sayHi = function(){ console.log("Hi"); } //報錯
建立函數的兩種方式,一個是函數聲明(如第一種方式);一個是函數表達式(如第二種方式)。第二種函數建立方式建立的函數叫「匿名函數」或「拉姆達函數」,由於function 關鍵字後面沒有標識符。node
須要注意的是,做爲對比,下面的兩種代碼中,第一個是錯誤的(會致使各瀏覽器出現不一樣的問題);第二個才使正確的。代碼以下:webpack
var condition = true; if (condition){ function sayHI(){ console.log("hi") } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 sayHI(); //"hello" }else{ function sayHI(){ console.log("hello") } sayHI(); }
報錯web
var condition = false; var sayHi; if(condition){ sayHi = function(){ console.log("hi") }; //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 sayHi(); }else{ sayHi = function(){ console.log("hello") }; sayHi(); //hello }
沒有錯誤面試
var condition = true; if(condition){ var sayHi = function(){ console.log("hi") }; sayHi(); //hi }else{ var sayHi = function(){ console.log("hello") }; sayHi(); //hello }
這裏也不會出現問題。出現上面問題的根源就是函數提高,就是函數聲明和函數表達式之間的區別所致使的。瀏覽器
給你們推薦一個技術交流學習圈,裏面歸納移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。獲取資料
對web開發技術感興趣的同窗,能夠加入交流圈👉👉👉1007317281,無論你是小白仍是大牛都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時天天更新視頻資料。閉包
遞歸函數就是在一個函數經過名字調用自身的狀況下構成的。如:
function factorial(num){ if(num <= 1){ return 1; }else{ return num * factorial(num - 1); } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 } console.log(factorial(4)); //24 4*3*2*1
可是,函數裏面包含了函數自身因此,在應該使用arguments.callee
來解決該問題。如:
function factorial(num){ if(num <= 1){ return 1; }else{ return num * arguments.callee(num - 1); } } console.log(factorial(4)); //24 4*3*2*1
但若是使用上面這種方法,則在嚴格模式下行不通。不過可使用命名函數表達式來達成相同的結果。如:
var factorial = ( function f(num){ if(num <= 1){ return 1; }else{ return num * f(num - 1); } } ); console.log(factorial(4)); //24 4*3*2*1
即,把它包含在一個變量裏面,在遇到其餘這種須要使用arguments.callee
的時候均可以這樣作。
閉包就是有權訪問另外一個函數做用域中的變量的函數。常見的建立閉包的方式就是在一個函數內部建立另外一個函數。在此以前應該先掌握做用域鏈的概念(見《Javascript執行環境和做用域的注意要點》一文)
如下面的代碼爲例
function compare(value1,value2){ if (value1 > value2){ return 1; }else if (value1 < value2){ return -1; }else { return 0; } } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 var result = compare(5,10);
調用函數compare
時,函數執行環境中的做用域鏈:
做用域鏈本質上是一個指向變量對象的指針列表。
另外一個例子:
function createComparisonFunction(propertyName){ return function(object1,object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; if (value1 < value2){ return -1; }else if(value1 > value2){ return 1; }else{ return 0; } }; } var compare = createComparisonFunction("name"); var result = compare({name: "Nicholas"},{name: "Greg"});
這個就至關於:
var compare = function(object1,object2){ var value1 = object1["name"]; var value2 = object2["name"]; if (value1 < value2){ return -1; }else if(value1 > value2){ return 1; }else{ return 0; } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 }; var result = compare({name: "Nicholas"},{name: "Greg"});
至關於:
var result = function(){ var value1 = {name: "Nicholas"}["name"]; var value2 = {name: "Greg"}["name"]; if (value1 < value2){ return -1; }else if(value1 > value2){ return 1; }else{ return 0; } }; console.log(result()); //1
因此,完整的代碼以下:
var compareNames = createComparisonFunction("name"); function createComparisonFunction(propertyName){ return function(object1,object2){ var value1 = object1[propertyName]; var value2 = object2[propertyName]; if (value1 < value2){ return -1; }else if(value1 > value2){ return 1; }else{ return 0; } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 }; } var result = compareNames({name: "Nicholas"},{name: "Greg"}); compareNames = null;
調用compareNames()
函數的過程當中產生的做用域鏈之間的關係圖以下:
常見的閉包的模式通常都是這樣:
var X = function A(a){ return function(b1,b2){...a...} //匿名函數 }; var Y = X(b1,b2);
舉個栗子:
var obj1 = { name: "co", color: ["white","black"] }; var obj2 = { name: "lor", color: ["yellow","red"] }; function displayProperty(propertyName){ return function(obj1,obj2){ var value1 = obj1[propertyName]; var value2 = obj2[propertyName]; if(typeof value1 === "string"){ return value1 + " and " + value2 + "<br/>"; }else if(value1 instanceof Array){ return value1.toString() + "<br/>" + value2.toString(); }else{ return false; } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 }; } var displayP = displayProperty("name"); var displayStr = displayP(obj1,obj2); document.write(displayStr); displayP = null; var displayP = displayProperty("color"); var displayStr = displayP(obj1,obj2); document.write(displayStr); /* co and lor white,black yellow,red */
閉包會攜帶他包含的函數的做用域,所以會更多的佔用內存資源,建議只有在絕對必要的時候再考慮使用閉包。V8 優化後的js 引擎會嘗試收回被閉包占用的內存。
閉包的反作用是閉包只能取得包含函數中任何變量的最後一個值。
this
對象全局函數中this = window;當函做爲位某個對象的方法調用時this = 那個對象;可是匿名函數的執行環境又全局性,this 一般指向window;但也有例外:
var name = "the window"; var obj = { name: "the obj", getNameFunc: function(){ return function(){ return this.name; }; } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 }; console.log(obj.getNameFunc()()); //"the window" 別忘了要寫兩個小括號
由於每一個函數在被調用的時候都會自動取得兩個特殊的變量:this 和arguments;內部函數在搜索這兩個變量時,只會搜索到其活動對象爲止。
var obj = { name: "Oliver", age: 18, friends: ["alice","troy"], sayName: function(){ return this.name; }, sayFriends: function(){ return function(){ return this.friends; }; } }; console.log(obj.sayFriends()()); //undefined
上面這個代碼就是由於閉包的問題,致使錯誤。又如:
var friends = "window's friends"; var obj = { name: "Oliver", age: 18, friends: ["alice","troy"], sayName: function(){ return this.name; }, sayFriends: function(){ return function(){ return this.friends; }; } //歡迎加入前端全棧開發交流圈一塊兒學習交流:1007317281 }; console.log(obj.sayFriends()()); //window's friends 匿名函數執行環境的全局性
解決這個問題的方法是:
var friends = "window's friends"; var obj = { name: "Oliver", age: 18, friends: ["alice","troy"], sayName: function(){ return this.name; }, sayFriends: function(){ var outer = this; return function(){ return outer.friends; }; } }; console.log(obj.sayFriends()()); //["alice","troy"] 這樣就能夠正常搜索到了
又如:
var friends = "window's friends"; var obj = { name: "Oliver", age: 18, friends: ["alice","troy"], sayWindowFriends: function(){ return function(){ return this.friends; }; }, sayFriends: function(){ var outer = this; return function(){ return function(){ return function(){ return outer.friends }; }; }; } }; console.log(obj.sayWindowFriends()()); //"window's friends" console.log(obj.sayFriends()()()()); //["alice", "troy"]
再舉個栗子:
var obj = { name: "Oliver", age: 18, friends: ["alice","troy"], sayFriends: function(i){ var outer = this; return function(j){ return outer.friends[i] + outer.friends[j]; }; } }; console.log(obj.sayFriends(0)(1)); //alicetroy
另外,在幾種特殊狀況下,this 的值可能會發生改變。如:
var name = "the window"; var obj = { name: "my object", getName: function(){ return this.name; } }; console.log(obj.getName()); //my object console.log((obj.getName)()); //my object 與上面的是相同的,多了個括號而已 console.log((obj.getName = obj.getName)()); //the window
只要不用下面兩種方式調用函數便可。
通過上面的一番折騰,最明顯的就是window.name 一直佔用內存,沒法清空。必須手動把它清理掉,用window.name = null
來操做。
感謝您的觀看,若有不足之處,歡迎批評指正。
本次給你們推薦一個免費的學習羣,裏面歸納移動應用網站開發,css,html,webpack,vue node angular以及面試資源等。獲取資料👈👈👈
對web開發技術感興趣的同窗,歡迎加入Q羣:👉👉👉1007317281👈👈👈,無論你是小白仍是大牛我都歡迎,還有大牛整理的一套高效率學習路線和教程與您免費分享,同時天天更新視頻資料。最後,祝你們早日學有所成,拿到滿意offer,快速升職加薪,走上人生巔峯。