摘要:不在任何函數(shù)內(nèi)聲明的變量為全局變量。加變成一個(gè)函數(shù)表達(dá)式,解釋器運(yùn)行創(chuàng)建一個(gè)匿名的函數(shù)表達(dá)式閉包終于到閉包了。
函數(shù)的實(shí)參和形參
可選形參if(a === undefined) a = [];
等價(jià)于
a = a || [];
這兩句是完全等價(jià)的,只不過后者需要提前聲明a而已
如果參數(shù)沒有傳入,其余的填充undefined
可選的形式參數(shù):通過注釋/optional/來強(qiáng)調(diào)參數(shù)可選,并且要將其放在最后,否則就要使用null或者undefined來作為占位符來進(jìn)行傳入
callee為指代當(dāng)前正在執(zhí)行的函數(shù)
caller指代當(dāng)前正在執(zhí)行函數(shù)的函數(shù)
> > function e(o){ ... return o.Object; ... } undefined > e; [Function: e] > var a = {Object:33}; undefined > e(a); 33 >作為值的函數(shù)
函數(shù)能作為值傳入另外一個(gè)函數(shù)
自定義函數(shù)屬性函數(shù)屬性可以自定義
o.a = 3; function o() { return o.a; }作為命名空間的函數(shù)
在函數(shù)中聲明的變量在整個(gè)函數(shù)體內(nèi)都是可見的(包括在嵌套函數(shù)中),在函數(shù)外部是不可見的。不在任何函數(shù)內(nèi)聲明的變量為全局變量。在整個(gè)js程序中都是可見的。在js中無法聲明只在一個(gè)代碼塊內(nèi)可見的變量的。所以常常簡單的定義一個(gè)函數(shù)用作臨時(shí)的命名空間。在這個(gè)命名空間內(nèi)定義的變量都不會(huì)污染到全局命名空間。
正是因?yàn)槿绻麑⒆兞慷x在全局的時(shí)候,會(huì)出現(xiàn)變量的污染,污染到全局變量(好吧,這是動(dòng)態(tài)語言的坑)導(dǎo)致出現(xiàn)一些未知的錯(cuò)誤。所以呢將變量放置在函數(shù)中,在進(jìn)行調(diào)用,這樣放置其變量污染其全局空間,出現(xiàn)變量的沖突(尤其是在瀏覽器的環(huán)境下,很容易的,導(dǎo)致各種未知錯(cuò)誤,所以必須要這樣做)
( function() { return 333; }() );
加()是必須的,因?yàn)槿绻患樱ǎ?huì)讓js解釋器認(rèn)為其為函數(shù)聲明,function按照函數(shù)聲明來進(jìn)行解釋,js解釋器不允許創(chuàng)建一個(gè)匿名的函數(shù)聲明,所以會(huì)報(bào)錯(cuò)。
加()變成一個(gè)函數(shù)表達(dá)式,js解釋器運(yùn)行創(chuàng)建一個(gè)匿名的函數(shù)表達(dá)式
終于到閉包了。(正經(jīng)點(diǎn)Σ( ° △ °|||)︴)
(這是最難的地方,是函數(shù)式編程的基礎(chǔ),也是能否學(xué)好js的最關(guān)鍵的地方。。。。當(dāng)然了,es6還有一個(gè)令人討厭的箭頭函數(shù))
閉包是其函數(shù)式編程的重要的基礎(chǔ)
和其他語言一樣js采用的詞法作用域,即函數(shù)的執(zhí)行依賴于變量的作用域,作用域是在函數(shù)定義時(shí)確定的,不是在其調(diào)用所決定的
即js的函數(shù)對(duì)象的內(nèi)部狀態(tài)不僅僅包含函數(shù)的代碼邏輯,還必須引用當(dāng)前的作用域鏈(變量的作用域向下傳遞的,變量的作用域鏈在進(jìn)行尋找的時(shí)候往上尋找,直到函數(shù)的頂部)函數(shù)對(duì)象可以通過作用域鏈相互關(guān)聯(lián)起來,函數(shù)體內(nèi)部的變量可以保存在函數(shù)作用域內(nèi),即閉包
很古老滴術(shù)語,指函數(shù)變量可以被隱藏于作用域鏈之內(nèi),因此看起來函數(shù)將變量包裹起來。如何定義作用域鏈
作用域鏈為一個(gè)對(duì)象的列表,每次調(diào)用js函數(shù)的時(shí)候,都會(huì)創(chuàng)建一個(gè)新的對(duì)象來保存其局部變量,把這個(gè)對(duì)象添加到作用域鏈中,如果函數(shù)返回,就從作用域鏈中將綁定的對(duì)象刪除,如果不存在嵌套函數(shù),也不存在其引用指向這個(gè)綁定的對(duì)象,會(huì)被js解釋器的垃圾回收機(jī)制不定時(shí)的回收,是不定時(shí)的,不是在沒有完全引用的時(shí)候立馬刪除,如果定義了嵌套函數(shù),每個(gè)嵌套函數(shù)都各自對(duì)應(yīng)著一個(gè)作用域鏈,并且這個(gè)作用域鏈指向一個(gè)變量綁定的對(duì)象。如果這些嵌套函數(shù)對(duì)象在外部函數(shù)中保存下來,那么他們也會(huì)和所指向的變量綁定對(duì)象一樣當(dāng)做垃圾進(jìn)行回收,如果這個(gè)函數(shù)定義了嵌套的函數(shù),并將它作為返回值返回,或者存儲(chǔ)在某處屬性里,會(huì)有外部引用指向這個(gè)嵌套函數(shù),即不會(huì)被當(dāng)做垃圾回收,其變量所綁定的對(duì)象也不會(huì)當(dāng)做垃圾進(jìn)行回收。
函數(shù)執(zhí)行完畢以后相關(guān)的作用域鏈不會(huì)刪除,只有當(dāng)不在有引用的時(shí)候,才會(huì)進(jìn)行刪除操作關(guān)于棧的說明
原始棧
棧頂 window
執(zhí)行下列js腳本
function a() { function f() { return 333; } return f; } a()();
棧頂 a → window
開始調(diào)用,執(zhí)行到return
發(fā)現(xiàn)需要調(diào)用f
繼續(xù)加棧
棧頂 f → a → window
執(zhí)行完f彈出f
繼續(xù)執(zhí)行a,執(zhí)行完畢彈出a
最后全部執(zhí)行完畢彈出window
算了文字解釋太無力,直接上代碼
var scope = "global scope"; // 一個(gè)全局變量 function checkscope() { var scope = "local scope"; // 定義一個(gè)局部變量 function f() { return scope; // 返回變量作用域中的scope的值 } return f(); // 返回這個(gè)函數(shù) }
調(diào)用一下這個(gè)函數(shù)
checkscope(); "local scope"
接著這樣執(zhí)行
var scope = "global scope"; // 一個(gè)全局變量 function checkscope() { var scope = "local scope"; // 定義一個(gè)局部變量 function f() { return scope; // 返回變量作用域中的scope的值 } return f; // 返回這個(gè)函數(shù) }
繼續(xù)調(diào)用函數(shù)
checkscope()(); "local scope"閉包有什么用
先看一個(gè)函數(shù)uniqueInteger()使用這個(gè)函數(shù)能夠跟蹤上次的返回值
var uniqueInteger = ( function() { var count = 0; return function() {return count++} }() );
這樣子就使用閉包
uniqueInteger(); 0 uniqueInteger(); 1
每次返回是其上一次的值,并隨便直接將值加1
至于為什么要這樣寫,如果不使用閉包,那么惡意代碼就可以隨便的將計(jì)數(shù)器重置了。。
uniqueInteger.count = 0; function uniqueInteger() { return uniqueInteger.count++; }
類似這樣的,完全可以做到直接通過賦值,將其count的值重置。
而如果使用閉包,沒有辦法進(jìn)行修改,為私有狀態(tài),也不會(huì)導(dǎo)致其一個(gè)頁面內(nèi)變量的沖突,或者是其覆蓋。
var a = (function c(){ var a = 1; a++; console.log("已經(jīng)執(zhí)行"); return function b(){return a++}; }())
額,我大概解釋一下這段代碼。
首先呢,解釋最外層的圓括號(hào),因?yàn)槿绻麤]有圓括號(hào),則這個(gè)是一個(gè)賦值語句,將一個(gè)匿名函數(shù)賦值給變量a,實(shí)際上是在內(nèi)存中完成了棧中變量a指向匿名函數(shù)存儲(chǔ)的空間的地址,如果有圓括號(hào),實(shí)際上是告訴js解釋器這是一個(gè)語句,需要js執(zhí)行,消除了其function帶來的影響。(ps;貌似火狐上不加也可以,也可以正常的運(yùn)行)執(zhí)行和引用的關(guān)系下方有。
然后呢,最后的圓括號(hào),代表著其執(zhí)行這個(gè)函數(shù),因?yàn)閖s解析器將()解析為調(diào)用前方的函數(shù)名稱,類似于運(yùn)算符吧。但是實(shí)際上并不是運(yùn)算符,因?yàn)槟芡鋬?nèi)傳值,注意,這點(diǎn)是將其執(zhí)行的結(jié)果保存在堆中,并完成其指向
其后,當(dāng)直接輸入a;,實(shí)際上執(zhí)行并完成了一次調(diào)用,其返回值為函數(shù)b,將函數(shù)b完成一次引用,即變量a引用函數(shù)b,由于其存在引用關(guān)系,即棧中變量a保存的為其函數(shù)a的返回結(jié)果,(因?yàn)槠洳皇遣皇菍?duì)象,如果寫a()()表示將函數(shù)a調(diào)用后返回的對(duì)象保存在棧中,然后將棧中的內(nèi)容再次調(diào)用,由于是保存,并不存在其應(yīng)用關(guān)系,所以執(zhí)行完畢后直接垃圾回收)由于其保存的是函數(shù)b的作用域鏈,而函數(shù)b的作用域鏈?zhǔn)抢^承自函數(shù)a的作用域鏈,但是由于函數(shù)a的作用域鏈并沒有引用導(dǎo)致其執(zhí)行完后被垃圾回收(當(dāng)不在有變量指向的時(shí)候)。所以呢,函數(shù)其值是在函數(shù)b中進(jìn)行保存,如果修改函數(shù)c此時(shí)函數(shù)c并不會(huì)影響到函數(shù)b中的保存,因?yàn)槠浜瘮?shù)c的變量列表已被銷毀,
最后,繼續(xù)討論起嵌套函數(shù)的引用,由于其父函數(shù)已被銷毀,但是嵌套函數(shù)被引用,(注意:因?yàn)槠涓敢呀?jīng)沒有,所以是另開辟一塊新的堆空間,用于存儲(chǔ)其函數(shù)c的返回結(jié)果,注意是返回結(jié)果,而不是函數(shù)b)此時(shí)另外指定變量保存其結(jié)果,無論指定多少個(gè)變量保存其結(jié)果,都是新的空間的執(zhí)行,沒有任何的干擾,詳細(xì)了解看下面,繼續(xù)討論
ps;如果是()()則代表其會(huì)被其垃圾回收
ps 還需要注意一點(diǎn)點(diǎn)的是由于其引用的是result的值,并不是其
最后,這樣就能完成其變量保存在函數(shù)中,貌似叫做記憶?
所以呢,借助堆和棧就很好的能理解了閉包
再繼續(xù)看代碼function count() { var n = 0; return { count: function() { return n++; }, reset: function() { n = 0; } }; }
var c = count(); var d = count(); undefined
在分別執(zhí)行一下下
c.count(); 0 d.count(); 0 c.count(); 1 d.count(); 1 c.reset(); undefined c.count(); 0 d.count(); 2
這一點(diǎn)體現(xiàn)了其互不影響性,表明其由于其父被回收,導(dǎo)致其子分別開創(chuàng)了一塊在堆中新的內(nèi)存空間,并完成其指向,互相不干擾。
其作用域鏈互不干擾
function count(n) { return { get count() { return n++; }, set count(m) { if ( m >= n) n = m; else throw new Error( "請(qǐng)輸入一個(gè)正確的值" ); }, }; }
這個(gè)就不用解釋啦,很簡單啦
同一個(gè)作用域鏈中定義兩個(gè)閉包function test1() { val = value = 111; this.test = function() { return value - 1; }; this.test2 = function() { return value + 1; }; }
這同樣是兩個(gè)作用鏈域
不過這樣寫需要先執(zhí)行其o.test1(),因?yàn)槠浞椒ㄔ谄浜瘮?shù)內(nèi)部,必須先執(zhí)行一下,完成其方法的添加,否則會(huì)報(bào)錯(cuò),
ee.test is not a function
提示找不到這個(gè)方法,
因?yàn)閳?zhí)行
ee.test1 = test1; function test1()
只是簡單的進(jìn)行賦值,并不能進(jìn)行查看,所以導(dǎo)致其無法使用
所以嘛,要先執(zhí)行一遍,讓其方法添加進(jìn)去
ee.test1(); undefined ee.test(); 110 ee.test2(); 112
這就是兩個(gè)閉包,這兩個(gè)閉包互相平行,同時(shí)繼承于其父,但是又不受其父影響,很神奇吧,(@ο@)
叮 又發(fā)現(xiàn)一個(gè)莫名奇妙的東東 https://us.leancloud.cn 貌似目前水平能看懂一些了關(guān)于this的問題
this在父閉包顯示的即為使用該方法的對(duì)象。
但是子就不一定了。
function test1() { val = value = 111; this.test = function() { return this.x - 1; }; this.test2 = function() { return this.x + 1; }; }
執(zhí)行一下
ee.test(); 4443
這就尷尬了。
好吧。只能說是一般不這樣用
一般這樣寫
var self = this;
將其值保存進(jìn)一個(gè)self中
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/96149.html
摘要:比如,以對(duì)象的方法的形式調(diào)用函數(shù)并傳入兩個(gè)參數(shù)可以傳入的參數(shù)可以是數(shù)組和類數(shù)組的。方法的該方法主要作用是將函數(shù)綁定至某個(gè)對(duì)象,方法返回一個(gè)新的函數(shù),調(diào)用這個(gè)新的函數(shù)會(huì)把綁定的函數(shù)在對(duì)象中當(dāng)做方法來調(diào)用。 參數(shù) 形參(parameter):函數(shù)中定義的變量 實(shí)參(argument):運(yùn)行時(shí)的函數(shù)調(diào)用時(shí)傳入的參數(shù) 上下文(context):通過對(duì)象來調(diào)用函數(shù)時(shí),這個(gè)對(duì)象就是thi...
摘要:但是,對(duì)函數(shù)式編程而言,這個(gè)行為的重要性是毋庸置疑的。關(guān)于該模式更正式的說法是偏函數(shù)嚴(yán)格來講是一個(gè)減少函數(shù)參數(shù)個(gè)數(shù)的過程這里的參數(shù)個(gè)數(shù)指的是希望傳入的形參的數(shù)量。 原文地址:Functional-Light-JS 原文作者:Kyle Simpson-《You-Dont-Know-JS》作者 關(guān)于譯者:這是一個(gè)流淌著滬江血液的純粹工程:認(rèn)真,是 HTML 最堅(jiān)實(shí)的梁柱;分享,是...
摘要:若有函數(shù)名,則在函數(shù)體內(nèi)指代該函數(shù)本身,并且只存在于函數(shù)體中。返回值與普通函數(shù)相同。如果嵌套函數(shù)作為普通函數(shù)調(diào)用,則指向全局對(duì)象或者構(gòu)造函數(shù)調(diào)用在函數(shù)或者方法調(diào)用之前使用關(guān)鍵字,則為構(gòu)造函數(shù)調(diào)用。創(chuàng)建一個(gè)新的對(duì)象繼承構(gòu)造函數(shù)的屬性。 showImg(https://box.worktile.com/view/ddbade8c84bb41cdb20db15228584b8e?pid=4b...
摘要:每個(gè)函數(shù)表達(dá)式包括函數(shù)對(duì)象括號(hào)和傳入的實(shí)參組成。和作用都是動(dòng)態(tài)改變函數(shù)體內(nèi)指向,只是接受參數(shù)形式不太一樣。在定義函數(shù)時(shí),形參指定為一個(gè)對(duì)象調(diào)用函數(shù)時(shí),將整個(gè)對(duì)象傳入函數(shù),無需關(guān)心每個(gè)屬性的順序。 函數(shù) JavaScript中,函數(shù)指只定義一次,但可以多次被多次執(zhí)行或調(diào)用的一段JavaScript代碼。與數(shù)組類似,JavaScript中函數(shù)是特殊的對(duì)象,擁有自身屬性和方法 每個(gè)函數(shù)對(duì)象...
摘要:中函數(shù)是一等公民。小明小明調(diào)用函數(shù)時(shí),傳遞給函數(shù)的值被稱為函數(shù)的實(shí)參值傳遞,對(duì)應(yīng)位置的函數(shù)參數(shù)名叫作形參。所以不推薦使用構(gòu)造函數(shù)創(chuàng)建函數(shù)因?yàn)樗枰暮瘮?shù)體作為字符串可能會(huì)阻止一些引擎優(yōu)化也會(huì)引起瀏覽器資源回收等問題。 函數(shù) 之前幾節(jié)中圍繞著函數(shù)梳理了 this、原型鏈、作用域鏈、閉包等內(nèi)容,這一節(jié)梳理一下函數(shù)本身的一些特點(diǎn)。 javascript 中函數(shù)是一等公民。 并且函數(shù)也是對(duì)象,...
閱讀 2268·2021-11-22 13:52
閱讀 3950·2021-11-10 11:36
閱讀 1460·2021-09-24 09:47
閱讀 1115·2019-08-29 13:54
閱讀 3391·2019-08-29 13:46
閱讀 1970·2019-08-29 12:16
閱讀 2139·2019-08-26 13:26
閱讀 3491·2019-08-23 17:10