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