摘要:如調用函數聲明函數不會報錯使用函數表達式則不可以報錯創建函數的兩種方式,一個是函數聲明如第一種方式一個是函數表達式如第二種方式。第二種函數創建方式創建的函數叫匿名函數或拉姆達函數,因為關鍵字后面沒有標識符。
函數表達式的基本概念 name屬性和函數提升
首先,name屬性,通過這個屬性可以訪問到給函數指定的名字。(非標準的屬性)如:
function People(){}; console.log(People.name); //People
其次,函數聲明提升,意味著可以把函數聲明放在調用它的語句后面。如:
sayHi(); //調用函數 function sayHi(){ //聲明函數 console.log("Hi"); } //不會報錯
使用函數表達式則不可以:
sayHi(); var sayHi = function(){ console.log("Hi"); } //報錯
創建函數的兩種方式,一個是函數聲明(如第一種方式);一個是函數表達式(如第二種方式)。第二種函數創建方式創建的函數叫“匿名函數”或“拉姆達函數”,因為function 關鍵字后面沒有標識符。
函數提升的常見錯誤需要注意的是,作為對比,下面的兩種代碼中,第一個是錯誤的(會導致各瀏覽器出現不同的問題);第二個才使正確的。代碼如下:
var condition = true; if (condition){ function sayHI(){ console.log("hi") } sayHI(); //"hello" }else{ function sayHI(){ console.log("hello") } sayHI(); }
報錯
var condition = false; var sayHi; if(condition){ sayHi = function(){ console.log("hi") }; 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 }
這里也不會出現問題。出現上面問題的根源就是函數提升,就是函數聲明和函數表達式之間的區別所導致的。
函數的遞歸遞歸函數就是在一個函數通過名字調用自身的情況下構成的。如:
function factorial(num){ if(num <= 1){ return 1; }else{ return num * factorial(num - 1); } } 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執行環境和作用域的注意要點》一文《Javascript執行環境和作用域的注意要點》)
作用域鏈以下面的代碼為例
function compare(value1,value2){ if (value1 > value2){ return 1; }else if (value1 < value2){ return -1; }else { return 0; } } 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; } }; 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; } }; } 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 + "
"; }else if(value1 instanceof Array){ return value1.toString() + "
" + value2.toString(); }else{ return false; } }; } 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; }; } }; 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; }; } }; 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來操作。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/78219.html
摘要:模仿塊級作用域在塊級語句中定義的變量,實際上是包含函數中而非語句中創建的。避免對全局作用域產生不良影響這種方式可以通過創建私有作用域,避免對全局作用域產生不良影響。一般包括函數的參數局部變量和內部定義的其他函數。 模仿塊級作用域 在塊級語句中定義的變量,實際上是包含函數中而非語句中創建的。如: function outputNumbers(x){ for (var i = 0;...
摘要:單體內置對象單體內置對象就是開發人員不必顯式地實例化內置對象,因為他們已經實例化了。前面的章節討論過了大多數內置對象,還定義了兩個單體內置對象和。 單體內置對象 單體內置對象就是開發人員不必顯式地實例化內置對象,因為他們已經實例化了。前面的章節討論過了大多數內置對象,ECMA-262 還定義了兩個單體內置對象:Global 和Math。 Global 對象 所有在全局作用域中定義的屬性...
摘要:類型每個函數都是類型的實例。如以上代碼可行,是因為在代碼開始值錢,解析器就已經通過一個名為函數聲明提升的過程,讀取并將函數聲明添加到執行環境中去。也可同時使用函數聲明和函數表達式,但在瀏覽器中會出錯。 Function 類型 每個函數都是Function 類型的實例。函數名實際上就是一個指向函數對象的指針,不會與某個函數綁定。 函數聲明方式創建Function,語法如下: functi...
摘要:類型通過類型來支持正則表達式。如由于構造函數的模式參數是字符串,所以在某些情況下要對字符串進行雙重轉義。而第二個循環使用構造函數在每次循環沖創建正則表達式。如另外,還有個用于存儲捕獲組的構造函數屬性。 EegExp 類型 ECMAScript 通過RegExp 類型來支持正則表達式。語法如下: var expression = / pattern / flags; 每個正則表達式都可...
摘要:深入理解原型和閉包王福朋博客園深入理解原型和閉包一切都是對象原文鏈接本文要點一切引用類型都是對象,對象是屬性的集合。每個對象都有一個,可稱為隱式原型。另外注意,構造函數的函數名第一個字母大寫規則約定。 深入理解javascript原型和閉包 王福朋 - 博客園 —— 《 深入理解javascript原型和閉包》 1. 一切都是對象 原文鏈接:http://www.cnblogs.com...
閱讀 3937·2021-10-12 10:12
閱讀 2897·2021-09-10 11:18
閱讀 3681·2019-08-30 15:54
閱讀 2814·2019-08-30 15:53
閱讀 645·2019-08-30 13:54
閱讀 976·2019-08-30 13:21
閱讀 2267·2019-08-30 12:57
閱讀 1698·2019-08-30 11:10