摘要:如調(diào)用函數(shù)聲明函數(shù)不會報(bào)錯(cuò)使用函數(shù)表達(dá)式則不可以報(bào)錯(cuò)創(chuàng)建函數(shù)的兩種方式,一個(gè)是函數(shù)聲明如第一種方式一個(gè)是函數(shù)表達(dá)式如第二種方式。第二種函數(shù)創(chuàng)建方式創(chuàng)建的函數(shù)叫匿名函數(shù)或拉姆達(dá)函數(shù),因?yàn)殛P(guān)鍵字后面沒有標(biāo)識符。
函數(shù)表達(dá)式的基本概念 name屬性和函數(shù)提升
首先,name屬性,通過這個(gè)屬性可以訪問到給函數(shù)指定的名字。(非標(biāo)準(zhǔn)的屬性)如:
function People(){}; console.log(People.name); //People
其次,函數(shù)聲明提升,意味著可以把函數(shù)聲明放在調(diào)用它的語句后面。如:
sayHi(); //調(diào)用函數(shù) function sayHi(){ //聲明函數(shù) console.log("Hi"); } //不會報(bào)錯(cuò)
使用函數(shù)表達(dá)式則不可以:
sayHi(); var sayHi = function(){ console.log("Hi"); } //報(bào)錯(cuò)
創(chuàng)建函數(shù)的兩種方式,一個(gè)是函數(shù)聲明(如第一種方式);一個(gè)是函數(shù)表達(dá)式(如第二種方式)。第二種函數(shù)創(chuàng)建方式創(chuàng)建的函數(shù)叫“匿名函數(shù)”或“拉姆達(dá)函數(shù)”,因?yàn)閒unction 關(guān)鍵字后面沒有標(biāo)識符。
函數(shù)提升的常見錯(cuò)誤需要注意的是,作為對比,下面的兩種代碼中,第一個(gè)是錯(cuò)誤的(會導(dǎo)致各瀏覽器出現(xiàn)不同的問題);第二個(gè)才使正確的。代碼如下:
var condition = true; if (condition){ function sayHI(){ console.log("hi") } sayHI(); //"hello" }else{ function sayHI(){ console.log("hello") } sayHI(); }
報(bào)錯(cuò)
var condition = false; var sayHi; if(condition){ sayHi = function(){ console.log("hi") }; sayHi(); }else{ sayHi = function(){ console.log("hello") }; sayHi(); //hello }
沒有錯(cuò)誤
var condition = true; if(condition){ var sayHi = function(){ console.log("hi") }; sayHi(); //hi }else{ var sayHi = function(){ console.log("hello") }; sayHi(); //hello }
這里也不會出現(xiàn)問題。出現(xiàn)上面問題的根源就是函數(shù)提升,就是函數(shù)聲明和函數(shù)表達(dá)式之間的區(qū)別所導(dǎo)致的。
函數(shù)的遞歸遞歸函數(shù)就是在一個(gè)函數(shù)通過名字調(diào)用自身的情況下構(gòu)成的。如:
function factorial(num){ if(num <= 1){ return 1; }else{ return num * factorial(num - 1); } } console.log(factorial(4)); //24 4*3*2*1
但是,函數(shù)里面包含了函數(shù)自身所以,在應(yīng)該使用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
但如果使用上面這種方法,則在嚴(yán)格模式下行不通。不過可以使用命名函數(shù)表達(dá)式來達(dá)成相同的結(jié)果。如:
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
即,把它包含在一個(gè)變量里面,在遇到其他這種需要使用arguments.callee的時(shí)候都可以這樣做。
函數(shù)的閉包閉包就是有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。常見的創(chuàng)建閉包的方式就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)。在此之前應(yīng)該先掌握作用域鏈的概念(見《Javascript執(zhí)行環(huán)境和作用域的注意要點(diǎn)》一文《Javascript執(zhí)行環(huán)境和作用域的注意要點(diǎn)》)
作用域鏈以下面的代碼為例
function compare(value1,value2){ if (value1 > value2){ return 1; }else if (value1 < value2){ return -1; }else { return 0; } } var result = compare(5,10);
調(diào)用函數(shù)compare時(shí),函數(shù)執(zhí)行環(huán)境中的作用域鏈:
作用域鏈本質(zhì)上是一個(gè)指向變量對象的指針列表。
另一個(gè)例子:
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"});
這個(gè)就相當(dāng)于:
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"});
相當(dāng)于:
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;
調(diào)用compareNames()函數(shù)的過程中產(chǎn)生的作用域鏈之間的關(guān)系圖如下:
常見的閉包的模式一般都是這樣:
var X = function A(a){ return function(b1,b2){...a...} //匿名函數(shù) }; var Y = X(b1,b2);
舉個(gè)例子:
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 */
閉包會攜帶他包含的函數(shù)的作用域,因此會更多的占用內(nèi)存資源,建議只有在絕對必要的時(shí)候再考慮使用閉包。V8 優(yōu)化后的js 引擎會嘗試收回被閉包占用的內(nèi)存。
閉包與變量閉包的副作用是閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值。
this對象全局函數(shù)中this = window;當(dāng)函作為位某個(gè)對象的方法調(diào)用時(shí)this = 那個(gè)對象;但是匿名函數(shù)的執(zhí)行環(huán)境又全局性,this 通常指向window;但也有例外:
var name = "the window"; var obj = { name: "the obj", getNameFunc: function(){ return function(){ return this.name; }; } }; console.log(obj.getNameFunc()()); //"the window" 別忘了要寫兩個(gè)小括號
因?yàn)槊總€(gè)函數(shù)在被調(diào)用的時(shí)候都會自動取得兩個(gè)特殊的變量:this 和arguments;內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí),只會搜索到其活動對象為止。
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
上面這個(gè)代碼就是因?yàn)殚]包的問題,導(dǎo)致錯(cuò)誤。又如:
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 匿名函數(shù)執(zhí)行環(huán)境的全局性
解決這個(gè)問題的方法是:
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"]
再舉個(gè)例子:
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 的值可能會發(fā)生改變。如:
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 與上面的是相同的,多了個(gè)括號而已 console.log((obj.getName = obj.getName)()); //the window
只要不用下面兩種方式調(diào)用函數(shù)即可。
內(nèi)存泄露經(jīng)過上面的一番折騰,最明顯的就是window.name 一直占用內(nèi)存,無法清空。必須手動把它清理掉,用window.name = null來操作。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/78219.html
摘要:模仿塊級作用域在塊級語句中定義的變量,實(shí)際上是包含函數(shù)中而非語句中創(chuàng)建的。避免對全局作用域產(chǎn)生不良影響這種方式可以通過創(chuàng)建私有作用域,避免對全局作用域產(chǎn)生不良影響。一般包括函數(shù)的參數(shù)局部變量和內(nèi)部定義的其他函數(shù)。 模仿塊級作用域 在塊級語句中定義的變量,實(shí)際上是包含函數(shù)中而非語句中創(chuàng)建的。如: function outputNumbers(x){ for (var i = 0;...
摘要:單體內(nèi)置對象單體內(nèi)置對象就是開發(fā)人員不必顯式地實(shí)例化內(nèi)置對象,因?yàn)樗麄円呀?jīng)實(shí)例化了。前面的章節(jié)討論過了大多數(shù)內(nèi)置對象,還定義了兩個(gè)單體內(nèi)置對象和。 單體內(nèi)置對象 單體內(nèi)置對象就是開發(fā)人員不必顯式地實(shí)例化內(nèi)置對象,因?yàn)樗麄円呀?jīng)實(shí)例化了。前面的章節(jié)討論過了大多數(shù)內(nèi)置對象,ECMA-262 還定義了兩個(gè)單體內(nèi)置對象:Global 和Math。 Global 對象 所有在全局作用域中定義的屬性...
摘要:類型每個(gè)函數(shù)都是類型的實(shí)例。如以上代碼可行,是因?yàn)樵诖a開始值錢,解析器就已經(jīng)通過一個(gè)名為函數(shù)聲明提升的過程,讀取并將函數(shù)聲明添加到執(zhí)行環(huán)境中去。也可同時(shí)使用函數(shù)聲明和函數(shù)表達(dá)式,但在瀏覽器中會出錯(cuò)。 Function 類型 每個(gè)函數(shù)都是Function 類型的實(shí)例。函數(shù)名實(shí)際上就是一個(gè)指向函數(shù)對象的指針,不會與某個(gè)函數(shù)綁定。 函數(shù)聲明方式創(chuàng)建Function,語法如下: functi...
摘要:類型通過類型來支持正則表達(dá)式。如由于構(gòu)造函數(shù)的模式參數(shù)是字符串,所以在某些情況下要對字符串進(jìn)行雙重轉(zhuǎn)義。而第二個(gè)循環(huán)使用構(gòu)造函數(shù)在每次循環(huán)沖創(chuàng)建正則表達(dá)式。如另外,還有個(gè)用于存儲捕獲組的構(gòu)造函數(shù)屬性。 EegExp 類型 ECMAScript 通過RegExp 類型來支持正則表達(dá)式。語法如下: var expression = / pattern / flags; 每個(gè)正則表達(dá)式都可...
摘要:深入理解原型和閉包王福朋博客園深入理解原型和閉包一切都是對象原文鏈接本文要點(diǎn)一切引用類型都是對象,對象是屬性的集合。每個(gè)對象都有一個(gè),可稱為隱式原型。另外注意,構(gòu)造函數(shù)的函數(shù)名第一個(gè)字母大寫規(guī)則約定。 深入理解javascript原型和閉包 王福朋 - 博客園 —— 《 深入理解javascript原型和閉包》 1. 一切都是對象 原文鏈接:http://www.cnblogs.com...
閱讀 3925·2021-10-12 10:12
閱讀 2886·2021-09-10 11:18
閱讀 3673·2019-08-30 15:54
閱讀 2808·2019-08-30 15:53
閱讀 638·2019-08-30 13:54
閱讀 965·2019-08-30 13:21
閱讀 2260·2019-08-30 12:57
閱讀 1687·2019-08-30 11:10