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