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