JS函数表达式——函数递归、闭包
一:定義函數(shù)的方式:
1、函數(shù)聲明;2、函數(shù)表達式
函數(shù)聲明的重要特征:函數(shù)聲明提升,在執(zhí)行代碼之前會先讀取函數(shù)聲明。
sayHi(); function sayHi(){console.log("Hi"); } 復制代碼函數(shù)表達式必須先賦值。
二:遞歸
function fact(num){if(num <= 1){return 1;}else{return num*fact(num-1);} } 復制代碼下面代碼可能導致出錯
var another = fact; fact = null; console.log(another(4)); //出錯 復制代碼使用arguments.callee可以解決這個問題,arguments.callee是一個指向正在執(zhí)行的函數(shù)的指針。
function fact(num){if(num<=1){return 1;}else{return num*arguments.callee(num-1);} } 復制代碼在嚴格模式下,不能通過arguments.callee訪問,創(chuàng)建一個名為f()的函數(shù),將它賦值給fact。
var fact = (function f(num){if(num <= 1){return 1;}else{return num * f(num-1);} }); 復制代碼三、閉包
閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。當某個函數(shù)被調(diào)用時,會創(chuàng)建一個執(zhí)行環(huán)境及相應(yīng)的作用域鏈。然后,使用arguments和其他命名參數(shù)的值來初始化函數(shù)的活動對象。
一般來說,當函數(shù)執(zhí)行完畢后,局部活動對象就會被銷毀,內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的變量對象)。但是,閉包的情況有所不同。
function create(name){return function(){return name;} } 復制代碼create()函數(shù)在執(zhí)行完畢后,其活動對象不會被銷毀,因為匿名函數(shù)的作用域鏈仍然在引用這個活動對象。當create()函數(shù)返回后,其執(zhí)行環(huán)境的作用域鏈會被銷毀,但它的活動對象仍然會留在內(nèi)存中;直到匿名函數(shù)被銷毀后,create()活動對象才會被銷毀。
1、閉包和變量
作用域鏈的這種配置,使閉包只能取得包含函數(shù)中任何變量的最后一個值。閉包保存的是整個變量對象,而不是某個特殊的變量。
function create(){var result = new Array();for(var i =0;i<10;i++){result[i] = function(){return i;};}return result; } 復制代碼數(shù)組10個10,每個函數(shù)的作用域都保存著create()函數(shù)的活動對象,所以引用的都是同一個變量i。
創(chuàng)建一個匿名函數(shù)強制讓閉包的行為符合預期:
function create(){var result = new Array();for(var i=0;i<10;i++){result[i] = function(num){return function(){return num;};}(i);}return result; } 復制代碼這個函數(shù)中,沒有直接把閉包賦值給數(shù)組,而是定義了一個匿名函數(shù),并將立即執(zhí)行該匿名函數(shù)的結(jié)果賦給數(shù)組。這里的匿名函數(shù)有一個參數(shù)num,也就是最終函數(shù)要返回的值。在調(diào)用每個匿名函數(shù)時,我們傳入了變量i。由于函數(shù)參數(shù)是按值傳遞的,所以就會將變量i的當前值復制給參數(shù)num。而在這個匿名函數(shù)內(nèi)部,又創(chuàng)建并返回一個訪問num的閉包。這樣一來,result數(shù)組中的每個函數(shù)都有num變量的一個副本,因此就可以返回各自不同的數(shù)值了。
2、this對象
this對象是在運行時基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,函數(shù)被當作某個對象的方法調(diào)用時,this等于那個對象。匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此this通常指向window。
構(gòu)造函數(shù)當作普通函數(shù)調(diào)用時,this代表的是全局window對象。和new使用創(chuàng)建對象,指向當前的對象。
var name = 'this window'; var object = {name: 'my object',getName: function(){return function(){return this.name;};} }; alert(object.getName()()); //"this window" 復制代碼由于getName()返回一個函數(shù),因此調(diào)用object.getName()()就會立即調(diào)用它返回的函數(shù),結(jié)果就是返回一個字符串。
3、內(nèi)存泄漏
function assign(){var element = document.getElementById("someElement");element.onclick = function(){alert(element.id);}; } 復制代碼以上代碼創(chuàng)建一個作為element元素事件處理程序的閉包,而這個閉包又創(chuàng)建了一個循環(huán)引用。由于匿名函數(shù)保存了一個對assign()活動對象的引用,因此會導致無法減少element的引用數(shù)。只要匿名函數(shù)存在,element的引用數(shù)至少也是1,因此它占用的內(nèi)存就永遠不會被回收。
可通過以下解決:
function assign(){var element = document.getElementById("someElement");var id = element.id;element.onclick = function(){alert(id);};element = null; } 復制代碼閉包會引用包含函數(shù)的整個活動對象,而其中包含著element。即使不直接引用element,包含函數(shù)的活動對象中也仍然會保存一個引用。
四、模仿塊級作用域
JS沒有塊級作用域的概念。在塊語句中定義的變量,實際是在包含函數(shù)中而非語句中創(chuàng)建的。
function output(count){for(var i=0;i<count;i++){alert(i);}var i; //重新聲明變量,視而不見alert(i); } 復制代碼在java、c++語言中,變量i只會在for循環(huán)的語句中有定義,循環(huán)一旦結(jié)束,變量i就會被銷毀。在JS中,變量i定義在output()的活動對象中,從它又定義開始就可以在函數(shù)內(nèi)部訪問它。
用作塊級作用域的(私有作用域)的匿名函數(shù)的語法:
(function(){//這里是塊級作用域 })(); 復制代碼重寫output()函數(shù):
function output(count){(function(){for(var i=0;i<count;i++){alert(i);}})();alert(i); //導致一個錯誤 } 復制代碼五、私有變量
轉(zhuǎn)載于:https://juejin.im/post/5b004abd6fb9a07aa925fead
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的JS函数表达式——函数递归、闭包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阿里云E-HPC赋能制造业仿真云弹性
- 下一篇: 解决SpringBoot使用Quartz