一、自定義函式function
函式就是功能、方法的封裝。函式能夠幫我們封裝一段程式程式碼,這一段程式碼會具備某一項功能,函式在執行時,封裝的這一段程式碼都會執行一次,實現某種功能。而且,函式可以多次呼叫。
1.1函式的定義和呼叫
語法:
定義:把需要實現的功能預先做好 執行:需要的時候執行這個功能,而且還可以執行多次 |
定義:function myName(){} 執行:myName() |
【語法解釋】:
function 定義函式的關鍵字 myName 函式名稱 () 引數集 {} 函式體,執行的程式碼都放在{}裡面 |
多條語句,組成一個“語句軍團”,集體作戰。
//定義一個函式,函式就是一組語句的集合 function haha(){ console.log(1); console.log(2); console.log(3); console.log(4); } haha();//呼叫haha函式 haha();//呼叫haha函式 haha();//呼叫haha函式
函式必須先定義,然後才能呼叫。
定義一個函式,用關鍵字function來定義,function就是英語“功能”的意思。表示這裡面定義的語句,完成了一些功能。function後面有一個空格,後面就是函式名字,函式的名字也是關鍵字,命名規範和變數命名是一樣的。名字後面有一對兒圓括號,裡面放置引數。然後就是大括號,大括號裡面是函式的語句。
function 函式名稱(){
} |
函式如果不呼叫,裡面的語句一輩子都不執行,等於白寫。
呼叫函式的方法,就是函式名稱加(),()是一個運算子,表示執行一個函式。
函式名稱() |
一旦呼叫函式,函式內部的程式碼不管對錯,都會執行。
能感覺到,函式是一些語句的集合,讓語句稱為一個軍團,集體作戰。要不出動都不出動,要出動就全動。
函式的意義1:在出現大量程式程式碼相同時候,可以為它門封裝成一個function,這樣只呼叫一次,就能執行很多語句。
1.2函式的引數
定義在函式內部的語句,都是相同的,但是實際上可以通過“引數”這個東西,來讓語句有差別。
定義函式時,內部語句可能有一些懸而未決的量,就是變數,這些變數,要求在定義時都羅列在圓括號中:
function fun(a){ console.log("我第"+a+"次說你好!"); } fun(100); fun(1); fun(2); fun(3);
呼叫的時候,把這個變數真實的值,一起寫在括號裡,這樣隨著函式的呼叫,這個值也傳給了a變數引數。
羅列在function圓括號中的引數,叫做形式引數;呼叫時傳遞的數值,叫做實際引數。
引數可以有多個,用逗號隔開。
function sum(a,b){ console.log(a + b); } sum(3,5); //8 sum(8,11); //19 sum("5",12); //512 sum(10); //NaN,因為a的值是10,b沒有被賦值是undefined,10+undefined=NaN sum(10,20,30,40,88); //30,後面的引數沒有變數接收
函式的意義2:在呼叫函式時,不用關心函式內部的實現細節,甚至這個函式是你網上抄的,可以執行。所以這個東西,給我們團隊開發帶來了好處。
定義函式的時候,引數是什麼型別,不需要指定型別:
呼叫的時候,傳進去什麼型別,a、b變數就是什麼型別
sum("5",12); //512,做的是連字串運算 |
另外,定義和呼叫的時候引數個數可以不一樣多,不報錯。
sum(10); |
因為只給a變數賦值,b沒有被賦值,b被隱式的var。b的值是undefined,10+undefined=NaN
sum(10,20,30,40,88); //30 |
只有前面兩個引數被形參變數接收了,後面的引數沒有變數接收,就被忽略了。
//封裝一個函式,計算m+....+n的和 //比如m是4,n是15,4+5+6+7...+15 function sum(m,n){ var s = 0; //累加器,累加和 for(var i = m;i <= n;i++){ s+=i //s = s + i; } console.log(s); } sum(1,100); //計算1到100的和 sum(10,13); //計算10+11+12+13的和 sum(13,10); //輸出0,所以你就知道函式順序關機,定義順序是什麼,傳遞順序就是什麼。
1.3函式的返回值
函式可以通過引數來接收東西,還可以通過return關鍵字來返回值,“吐出”東西。
function sum(a,b){ return a+b; //現在這個函式的返回值就是a+b的和 } //sum沒有輸出功能,就要用console.log輸出 console.log(sum(3,8)); //計算sum(3,8);實際上稱為表示式,需要計算,計算後是11 console.log(sum(3,sum(4,5)));//輸出12,實際上兩次執行了sum函式,先執行內層的,計算出9,然後sum(3,9)就是12
函式有一個return的值,那麼現在這個函式,實際上是一個表示式,換句話說這個函式就是一個值。
所以這個函式,可以當做其他的函式引數。
sum(3,sum(4,5)); |
程式從內層執行到外層,sum(3,9)
函式可以接收很多值,但是返回一個值。
函式的意義3:模組化程式設計,讓複雜的邏輯變得更簡單。
函式只能有唯一的return,有if語句除外。
程式遇見return,會做兩件事:
1、立即返回結果,返回到呼叫它的地方
2、不執行return後面的程式碼。
function fun(){ console.log(1); console.log(2); //return; //返回一個空值,undefined return "你好啊!"; console.log(3); //這行語句不執行,因為函式已經return,所以會終止執行後面的程式碼。 } console.log(fun();
1.4函式模組化程式設計
實現前提:函式有返回值,可以作為其他函式執行時傳的實參。
習慣將複雜工作,進行一步步的分工,將一部分工作的結果作為下一步工作的條件。
將程式中某個單獨的功能製作成單獨函式,這就是造輪子的過程。
業務邏輯上:將所有的輪子進行拼裝。
將程式分成有層次的模組,製作過程中一部分函式要有返回值,執行結果作為另一些模組的引數、條件。
現在做一個程式,輸出2~100的所有質數,所謂的質數,就是隻有1和原數本身兩個約數,沒有其他約數。
把一個複雜的問題,拆分成一個個小問題,每個都是一個單獨的函式:
邏輯思維:約數個數函式 → 判斷質數函式 → 高層業務
程式設計需要逆向思維程式設計:製作約數個數函式 → 製作判斷質數函式 → 高層業務
函式思維找質數:
//約數個數函式:能夠傳入一個數字,返回它的約數個數 function yueshugeshu(a){ //計算這個數字的約數個數 var count = 0; //累加約數個數 for(var i = 1;i <= a;i++){ if(a % i == 0){ //判斷是否為約數 count++; } } return count; } //判斷是否是質數,如果一個函式名字取名為is,就暗示了將返回布林值 //要麼返回true,要麼返回false。 //接收一個引數m,返回是否為質數(true或false) function isZhishu(m){ if(yueshugeshu(m) == 2){ return true; }else{ return false; } } //尋找1~100的質數 for(var i = 1;i <= 100; i++){ if(isZhishu(i)){ //可以省略==true的判斷 //isZhishu()給我們返回了true和false console.log(i); } }
利用函式驗證哥德巴赫猜想:使用者輸入偶數拆分兩個質數和:
哥德巴赫猜想:任何一個偶數,都可以拆分為兩個質數的和。
現在要求,使用者輸入一個偶數,你把所有的質數拆分可能,寫出來。
比如:
4 = 2 + 2
6 = 3 + 3
8 = 3 + 5
48 = 5 + 43
程式碼見案例:
約數個數函式,裡面的細節不需要關心,它足夠的魯棒,就能返回約數個數。
上層的函式,可以使用下層的API:
//約數個數函式:能夠傳入一個數字,返回它的約數個數 function yueshugeshu(a){ //計算這個數字的約數個數 var count = 0; //累加約數個數 for(var i = 1;i <= a;i++){ if(a % i == 0){ //判斷是否為約數 count++; } } return count; } //判斷是否是質數,如果一個函式名字取名為is,就暗示了將返回布林值 //要麼返回true,要麼返回false。 //接收一個引數m,返回是否為質數(true或false) function isZhishu(m){ if(yueshugeshu(m) == 2){ return true; }else{ return false; } } //哥德巴赫猜想,使用者輸入一個數字 //驗證偶數是否能被拆分兩個質數的和,拆分的思想就是窮舉法 //比如使用者輸入48,那麼就: //看看1、47是不是都質數 //看看2、46是不是都質數 //看看3、45是不是都質數 //... var even = parseInt(prompt("請輸入一個偶數")); for(var i = 4;i < even;i++){ if(isZhishu(i) && isZhishu(even - i)){ console.log(even + "可以拆分為" + i + "與" + (even - i) + "的和"); } }
利用函式驗證哥德巴赫猜想-一百萬以內的偶數拆分:優化
function yueshugeshu(a){ ... } function isZhishu(m){ ... } //注意驗證,驗證偶數能否被拆成兩個質數 waiceng:for(var i = 4 ; i <= 1000000 ; i+=2){ for(var j = 2 ; j < i ; j++){ if(isZhishu(j) && isZhishu(i - j)){ console.log(i +"可以拆分為"+ j +"與"+ (i - j) + "的和"); continue waiceng; } } }
1.5函式遞迴
函式可以自己呼叫自己,就是遞迴。
function haha(){ console.log("哈哈"); haha();//呼叫自己 } haha(); function sum(a){ if(a == 1){ return 1; }else{ return a + sum(a-1); //10 + 9 + 8 + sum(7) } } console.log(sum(10));
斐波那契數列就是經典的遞迴演算法:
1 1、1、2、3、5、8、13、21、34、55、89、144、233... |
輸出斐波那契數列
只需要一個函式,就可以搞定全部問題。
fib(n); 就能得到第n位的數字
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
...
fib(10) = 55
function fib(n){ if(n == 1 || n == 2){ return 1; }else{ return fib(n - 1) + fib(n - 2); } } // console.log(fib(10)); for(var i = 1;i <= 50;i++){ console.log(fib(i)); }
1.6函式表示式
定義函式除了使用function之外,還有一種方法,就是函式表示式。就是函式沒有名字,稱為“匿名函式”,為了今後能夠呼叫它,我們把這個匿名函式,直接賦值給一個變數。
var haha = function(){ console.log("哈哈"); } // console.log(haha); haha(); //以後要呼叫這個函式,就可以直接使用haha變數呼叫。
等價於:
function haha(){ console.log("哈哈"); } haha();
如果這個函式表示式的function不是匿名,而是有名字的:
var haha = function xixi(){ console.log("哈哈"); } xixi(); //這是錯誤的 haha(); //這的對的
那麼JS表現非常奇怪,在外部只能用haha()呼叫,xixi()會引發錯誤。
也就是說,JS這個奇怪的特性,給我們提了個醒,定義函式,只能用以下兩種方法,不能雜糅:
function haha(){} |
var haha = function(){} |
錯誤的:
var haha = function xixi(){} |
1.7函式宣告的提升(預解析)
//先呼叫,可以輸出,因為有函式宣告提升的特性 fun(); fun(); fun(); //後定義 function fun(){ console.log("我是函式!"); }
不會報錯。
JS在執行前,會有一個預解析的過程,把所有的函式宣告和變數的宣告,都提升到了最開頭,然後再執行第一行程式碼。所以function定義在哪裡,都不重要,程式總能找到這個函式。
函式宣告頭可以提升,JS程式執行前,都會有一個函式預解釋階段,預解釋階段是自動進行的
函式優先:函式宣告和變數宣告都會被提升,但是面試常考的一個細節是:函式會被首先提升,然後才是變數。
函式提升是沒節操的,無視if等語句的判斷,強制提升
在JavaScript世界中,函式是一等公民。
函式宣告會被提升,但是函式表示式卻不會被提升:
fun(); var fun = function(){ //因為它是函式表示式,而不是function定義法 alert("我是函式!"); }
又給我們提了個醒,沒有特殊的理由,都要用function haha(){}來定義函式。
函式優先:
aaa(); //現在這個aa到底是函式,還是變數5? console.log(aaa);//函式優先,遇見同名的識別符號,預解析階段一定把這個識別符號給函式 var aaa = 5; //定義一個變數,是5 function aaa(){ alert("我是aaa函式!") }
面試題:
函式優先,現在foo這個識別符號衝突了,一個函式叫foo,一個變數也叫foo。預解析階段,如果遇見識別符號衝突,這個識別符號給函式。
1.8函式是一個引用型別
基本型別:Number、String、Boolean、undefined、null
引用型別:Object、function、array、RegExp、Math、Date。
function fun(){} var haha = function (){} console.log(typeof fun); //引用型別中的function型別 console.log(typeof haha);//引用型別中的function型別
函式也是一種型別,這個型別叫function,是引用型別的其中一種。
基本型別:儲存值
引用型別:儲存地址
現在變數a = 1,那麼這個a變數裡面儲存1這個數字
//基本型別的賦值 var a = 1; var b = a; //b得到的值是a的副本,a把自己複製了一份,給了b b = 3; //改變了b的值,a不受影響 console.log(a); //1 console.log(b); //3
//引用型別的賦值: //定義了一變數a,引用了一個function //這個a變數儲存的是這個匿名函式的記憶體地址 var a = function(){ alert("我是一根函式"); } var b = a; //就是把匿名函式的地址也給了b。 b.xixi = 1; //給b新增一個屬性 console.log(a.xixi); //輸出a的xixi屬性,a也有這個屬性了 //b的xixi屬性和a的變數都改變了,因為都是指向同一個物件(同一個記憶體地址) b.xixi++; b.xixi++; b.xixi++; console.log(a.xixi); console.log(b.xixi);
總結:
預解釋:在js中,程式碼從上到下執行之前,(瀏覽器預設)首先會把所有帶var和function關鍵字的進行提前宣告或者定義
var num=88;
宣告(declare):相當於種樹時"挖坑" var num; 只宣告沒有定義時,num的預設值是undefined
定義(defined):相當於種樹時"栽樹" num=88;(給變數賦值)
在預解釋的時候,帶var和帶function的還不一樣:
var:只是提前的宣告(定義賦值的部分是在程式碼執行的時候完成的)
function:提前的宣告+定義
在瀏覽器載入HTML頁面時,首先會開闢一個供js程式碼執行的環境-->"全域性作用域"(window/global)
棧記憶體(作用域):儲存基本資料型別的值;提供js程式碼執行的環境;
堆記憶體:在js中,對於引用資料型別來說,首先會開闢一個新的記憶體空間,然後把程式碼儲存到這個空間中,最後把空間的地址給相關的變數--->我們把新開闢的這個記憶體空間稱為"堆記憶體"。
堆記憶體的作用:儲存引用資料型別值
二、作用域
2.1函式能封閉住作業域
變數的作用域無非就兩種:全域性變數和區域性變數。
2.1.1全域性變數(全域性作用域)
全域性變數:在最外層函式定義的變數擁有全域性作用域,即對任何內部函式來說,都是可以訪問的。
言外之意:如果變數沒有定義在任何的function中,那麼它將在程式中任意範圍內都有定義:
var a = 100; //定義在全域性的變數,在程式任何一個角落都有定義 function fn(){ console.log("我是函式裡面的語句,我認識全域性變數a值為:" + a); } fn(); console.log("我是函式外面的語句,我認識全域性變數a值為:" + a);
2.1.2區域性變數(區域性作用域)
區域性變數:和全域性作用域相反,區域性作用域一般只在固定的程式碼片段內可訪問,而對於函式外部是無法訪問的。
例如:變數定義在function裡面,這個變數就是區域性變數,只在當前這個function函式內部能使用。在函式外部不能使用這個變數,出了這個function,就如同沒有定義過一樣。
function fn(){ var a = 100; //定義在函式的變數,區域性變數 console.log("我是函式裡面的語句,我認識變數a值為:" + a); } fn(); console.log("我是函式外面的語句,我認識變數a值為:" + a);
a被var在了function裡面,所以現在這個a變數只能在紅框範圍內有定義:
在ES5語法中,JavaScript變數作用域非常簡單,能關住作用域的只有一個,就是:函式。
【總結】:
● 定義在function裡面的變數,叫做區域性變數,只在function裡面有定義,出了function沒有定義的。
● 定義在全域性範圍內的,沒寫在任何function裡面的,叫做全域性變數,都認識。
【原理】:
全域性變數在定義時,就會直接生成一個新的變數,在任何位置查詢變數都有定義。
區域性變數定義在函式內部,函式如果不執行,相當於內部的程式碼沒寫,區域性變數等於從未定義過,在函式執行時,會在函式作用域內部立即定義了一個變數,使用完之後,變數立即被銷燬。所以在外部永遠找不到區域性變數定義。
2.2作用域鏈
作用域鏈:根據在內部函式可以訪問外部函式變數的這種機制,用鏈式查詢決定哪些資料能被內部函式訪問。
當遇見變數時,JS引擎會從其所在的作用域依次向外層查詢,查詢會在找到第一個匹配的識別符號時停止。
在私有作用域中出現了變數,首先看是否為私有的,如果是私有變數,那麼就用私有的即可。如果不是私有變數,則往當前作用域的上級作用域查詢,如果上級作用域也沒有,則繼續往上查詢....一直找到window為止。
//變數的作用域,就是它var的時候最內層的function function outer(){ var a = 1; //a的作用域是outer inner(); function inner(){ var b = 2; //b的作用域是inner console.log(a); //能輸出1,a在本層沒有定義,就往上找 console.log(b); //能輸出2 } } outer(); console.log(a); //報錯,因為a的作用域是outer
多層巢狀:如果有同名的變數,那麼就會發生“遮蔽效應”:
var a = 10; //全域性變數 function fn(){ console.log(a); //undefined,提升宣告瞭區域性變數 var a = 13; //把外層的a變數遮蔽了,這函式內部看不見外層的a console.log(a); //輸出13,變數在當前作用域尋找,找到a定義為13 } fn(); fn(); fn(); console.log(a); //10,變數在當前作用域尋找,找到全域性a
一個變數在使用的時候得幾?就會在當前作用域去尋找它的定義,找不到,去上一層找,直到找到全域性(window),如果全域性也沒有,就報錯。這就是作用域鏈。
題目:
var a = 1; //全域性變數 var b = 2; //全域性變數 function outer(){ var a = 3; //遮蔽了外層的a,a區域性變數 function inner(){ var b = 4; //遮蔽了外層的b,b區域性變數 console.log(a); //① 輸出3,a現在在當前層找不到定義的,所以就上一層尋找 console.log(b); //② 輸出4 } inner(); //呼叫函式 console.log(a); //③ 輸出3 console.log(b); //④ 輸出2 b現在在當前層找不到定義的,所以就上一層尋找 } outer(); //執行函式,控制權交給了outer console.log(a); // ⑤ 輸出1 console.log(b); // ⑥ 輸出2
2.3不寫var就自動成為全域性變數了
需要注意,函式內部宣告的時候,一定要用var命令,如果不用,實際上宣告瞭一個全域性變數。
function fn(){ a = 100;//這個a第一次賦值時,沒有var,所以就自動在全域性作用域var了一次 } fn(); console.log(a);//100 |
這是JS的機理,如果遇見一個識別符號,從來沒有var過,並賦值了:
a = 100; |
那麼就會自動在全域性作用域定義var a;
2.4函式的形參變數,會預設定義為這個函式的區域性變數
var a = 0; var b = 0; function fn(a,b){ a = 3; b = 4; console.log(a,b); } fn(); console.log(a); console.log(b);
a,b就是fn內部的區域性變數,只能在當前function函式內部使用,出了fn就沒有定義。
2.5全域性變數的作用
在函式內部使用自己的變數,儘量定義為區域性。
全域性變數有自己獨特的用途:累加、傳遞。
累加:函式沒執行一次,都要求變數在原來基礎上發生變化。
功能1:通訊,共同操作傳遞同一個變數
兩個函式同時操作一個變數,一個增加,一個減少,函式和函式通訊。
var num = 0; //全域性變數 function add(){ num++; } function remove(){ num--; } add(); add(); add(); add(); remove(); remove(); add(); add(); console.log(num); //4
功能2:累加,重複呼叫函式的時候,不會重置
var num = 0; function baoshu(){ num++; console.log(num); } baoshu();//1 baoshu();//2 baoshu();//3
如果num定義在baoshu裡面,每次執行完函式,作用域就被銷燬,所以裡面變數都是全新的。
function baoshu(){ var num = 0; num++; console.log(num); } baoshu(); //1 baoshu(); //1 baoshu(); //1
2.6函式定義也有作用域
function outer(){ var a = 10; function inner(){ //區域性函式 console.log("哈哈"); } } outer(); inner(); //報錯,因為全域性作用域下,沒有inner函式的定義 console.log(a);//報錯
公式:
function 大(){ function 小(){ } 小(); 可以執行 } 小(); //不能執行,因為小函式定義在大函式裡面,離開大函式就沒有作用域。
三、閉包
閉包有兩個作用:
1、可以讀取自身函式外部的變數(沿著作用域鏈尋找)
2、可以讓這些外部變數始終儲存在記憶體中
3.1閉包
推導過程:之前已經學習過,inner這個函式不能在outer外面呼叫,因為outer外面沒有inner定義。
當函式執行的時候,會形成一個新的私有作用域,來保護裡面的私有變數不受外界干擾,我們把函式的這種保護機制--->"閉包"。
function fn(){ } fn(); function outer(){ var a = 100; function inner(){ console.log(a); } } outer(); inner(); //在全域性作用域呼叫inner,全域性沒有inner的定義,所以報錯
但是我們就想在全域性作用域下,執行outer內部的inner,此時我們必須想一些奇奇怪怪的方法。
有一個簡單可行的方法,就是讓outer自己return掉inner。
非常經典的閉包案例,任何培訓機構、書、講閉包,一定是下面的案例:
function outer(){ var a = 888; function inner(){ console.log(a); //888 } return inner; //outer返回了inner的引用 } var inn = outer();//inn就是inner函式了 inn(); //執行inn,全域性作用域下沒有a的定義,但是函式閉包,能夠把定義函式時的作用域一起記憶住,輸出888
一個函式可以把自己內部的語句,和自己宣告時,所處的作用域一起封裝成了一個密閉的環境,就叫“閉包”。
每個函式都是閉包,每個函式天生都能夠記憶自己定義時所處的作用域環境。但是,我們必須將這個函式,挪到別的作用域,才能更好的觀察閉包。這樣才能實驗它有沒有把作用域給“記住”。
我們發現,把一個函式從它定義的那個作用域,挪走,執行。嘿,這個函式居然能夠記憶住定義時的那個作用域。不管函式走到哪裡,定義時的作用域就帶到了哪裡。這就是閉包。
閉包在工作中是一個用來防止產生隱患的事情,而不是加以利用的性質。
因為我們總喜歡在函式定義的環境中執行函式。從來不會把函式往外挪。那為啥學習閉包,防止一些隱患,面試絕對考。
使用全域性變數接收,返回函式:
var inn; //全域性變數 function outer(){ var a = 250; var b = 500; //全域性變數inn此時被賦值了一個函式 //這個函式此時將立即生成閉包,記憶住此時所處的環境 inn = function inner(){ console.log(a); console.log(b); } } outer(); var a = 300; var b = 400; inn();//一個函式在執行時,找閉包裡面的變數,不會理會當前作用域
閉包題目1:
function outer(x){ function inner(y){ console.log(x+y) } return inner; } var inn = outer(3);//接收到了inner函式,inn就是inner inn(5); //8 inn(7); //10 inn(10); //13
閉包題目2:
function fun1(x,y){ function fun2(x){ console.log(x + y); } return fun2; } var f = fun1(3,4); //f就代表fun2 f(); //NaN f(6); //10
一般情況下:當函式執行會形成一個私有的作用域(形參賦值→預解析→程式碼執行),當這三步都進行完成後,瀏覽器會剛剛開闢的這個私有作用域進行回收,也就是說,函式執行完成,作用域立即銷燬。
3.2閉包的性質
每次重新接收引用的函式時,閉包都是全新。
function outer(){ var count = 0; function inner(){ count++; console.log(count); } return inner; } var inn1 = outer(); var inn2 = outer(); //兩個變數引用的是同一個inner函式,實際上兩個引用變數中,都是全新閉包 inn1(); //1 inn1(); //2 inn1(); //3 inn1(); //4 inn2(); //1 inn2(); //2 inn1(); //5 inn1(); //6 inn1(); //7
無論它在何處被呼叫,它總是能訪問定義時所處作用域中的全域性變數。
每個新的函式,不管通過何種結構生成,閉包都是新的,作用域也是新的,語句也是新的。通過同一個結構組成兩個不同的函式,直接不會互相影響。
實際應用:要考慮閉包對程式造成影響,瞭解原因,平時不會寫個特殊結構。
3.3作用域銷燬的問題
在函式中,return後面返回的值如果是一個函式,這個函式是不參與預解釋的;函式體中return後面的程式碼也不執行,但是需要把後面的程式碼參預解釋;
銷燬的作用域:一般情況下,函式執行完成後,當前的作用域都立即銷燬;
不銷燬的作用域:
當函式執行時,在私有作用域中返回了一個引用資料型別的值(例如:函式、物件、陣列...等)
並且在函式外面,有變數接收了這個返回值,此時當前的這個私有作用域就被佔用了,這個作用域也不能銷燬了;
作用域不銷燬,裡面的私有變數也不再銷燬了。
不立即銷燬的作用域:
當函式執行時,在私有作用域中返回了一個引用資料型別的值(例如:函式、物件、陣列...等)
但是並沒有變數在函式的外面接收,那麼瀏覽器暫時先不銷燬,等到瀏覽器空閒的時候,會自己銷燬這個作用域。