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