国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

跟我學習JScript的Bug與內存管理

2019-11-20 11:14:40
字體:
來源:轉載
供稿:網友

1、JScript的Bug

IE的ECMAScript實現JScript嚴重混淆了命名函數表達式,搞得現很多人都出來反對命名函數表達式,而且即便是現在還一直在用的一版(IE8中使用的5.8版)仍然存在下列問題。

下面我們就來看看IE在實現中究竟犯了那些錯誤,俗話說知已知彼,才能百戰不殆。我們來看看如下幾個例子:

例1:函數表達式的標示符泄露到外部作用域

var f = function g(){};typeof g; // "function"

前面我們說過,命名函數表達式的標示符在外部作用域是無效的,但JScript明顯是違反了這一規范,上面例子中的標示符g被解析成函數對象,這就亂了套了,很多難以發現的bug都是因為這個原因導致的。

注:IE9以后貌似已經修復了這個問題

例2:將命名函數表達式同時當作函數聲明和函數表達式

typeof g; // "function"var f = function g(){};

特性環境下,函數聲明會優先于任何表達式被解析,上面的例子展示的是JScript實際上是把命名函數表達式當成函數聲明了,因為它在實際聲明之前就解析了g。

這個例子引出了下一個例子。

例3:命名函數表達式會創建兩個截然不同的函數對象!

var f = function g(){};f === g; // falsef.expando = 'foo';g.expando; // undefined

看到這里,大家會覺得問題嚴重了,因為修改任何一個對象,另外一個沒有什么改變,這太惡了。通過這個例子可以發現,創建2個不同的對象,也就是說如果你想修改f的屬性中保存某個信息,然后想當然地通過引用相同對象的g的同名屬性來使用,那問題就大了,因為根本就不可能。

再來看一個稍微復雜的例子:

例4:僅僅順序解析函數聲明而忽略條件語句塊

var f = function g() {  return 1;};if (false) { f = function g(){ return 2; };}g(); // 2

這個bug查找就難多了,但導致bug的原因卻非常簡單。首先,g被當作函數聲明解析,由于JScript中的函數聲明不受條件代碼塊約束,所以在這個很惡的if分支中,g被當作另一個函數function g(){ return 2 },也就是又被聲明了一次。然后,所有“常規的”表達式被求值,而此時f被賦予了另一個新創建的對象的引用。由于在對表達式求值的時候,永遠不會進入“這個可惡if分支,因此f就會繼續引用第一個函數function g(){ return 1 }。分析到這里,問題就很清楚了:假如你不夠細心,在f中調用了g,那么將會調用一個毫不相干的g函數對象。

你可能會問,將不同的對象和arguments.callee相比較時,有什么樣的區別呢?我們來看看:

var f = function g(){  return [  arguments.callee == f,  arguments.callee == g  ];};f(); // [true, false]g(); // [false, true]

可以看到,arguments.callee的引用一直是被調用的函數,實際上這也是好事,稍后會解釋。

還有一個有趣的例子,那就是在不包含聲明的賦值語句中使用命名函數表達式:

(function(){ f = function f(){};})();

按照代碼的分析,我們原本是想創建一個全局屬性f(注意不要和一般的匿名函數混淆了,里面用的是帶名字的聲明),JScript在這里搗亂了一把,首先他把表達式當成函數聲明解析了,所以左邊的f被聲明為局部變量了(和一般的匿名函數里的聲明一樣),然后在函數執行的時候,f已經是定義過的了,右邊的function f(){}則直接就賦值給局部變量f了,所以f根本就不是全局屬性。

了解了JScript這么變態以后,我們就要及時預防這些問題了,首先防范標識符泄漏帶外部作用域,其次,應該永遠不引用被用作函數名稱的標識符;還記得前面例子中那個討人厭的標識符g嗎?――如果我們能夠當g不存在,可以避免多少不必要的麻煩哪。因此,關鍵就在于始終要通過f或者arguments.callee來引用函數。如果你使用了命名函數表達式,那么應該只在調試的時候利用那個名字。最后,還要記住一點,一定要把命名函數表達式聲明期間錯誤創建的函數清理干凈。

2、JScript的內存管理

知道了這些不符合規范的代碼解析bug以后,我們如果用它的話,就會發現內存方面其實是有問題的,來看一個例子:

var f = (function(){ if (true) { return function g(){}; } return function g(){};})();

我們知道,這個匿名函數調用返回的函數(帶有標識符g的函數),然后賦值給了外部的f。我們也知道,命名函數表達式會導致產生多余的函數對象,而該對象與返回的函數對象不是一回事。所以這個多余的g函數就死在了返回函數的閉包中了,因此內存問題就出現了。這是因為if語句內部的函數與g是在同一個作用域中被聲明的。這種情況下 ,除非我們顯式斷開對g函數的引用,否則它一直占著內存不放。

var f = (function(){ var f, g; if (true) { f = function g(){}; } else { f = function g(){}; } // 設置g為null以后它就不會再占內存了 g = null; return f;})();

通過設置g為null,垃圾回收器就把g引用的那個隱式函數給回收掉了,為了驗證我們的代碼,我們來做一些測試,以確保我們的內存被回收了。

測試

測試很簡單,就是命名函數表達式創建10000個函數,然后把它們保存在一個數組中。等一會兒以后再看這些函數到底占用了多少內存。然后,再斷開這些引用并重復這一過程。下面是測試代碼:

function createFn(){ return (function(){ var f; if (true) {  f = function F(){  return 'standard';  }; } else if (false) {  f = function F(){  return 'alternative';  }; } else {  f = function F(){  return 'fallback';  }; } // var F = null; return f; })();}var arr = [ ];for (var i=0; i < 10000; i++) { arr[i] = createFn();}

通過運行在Windows XP SP2中的任務管理器可以看到如下結果:

IE7: without `null`: 7.6K -> 20.3K with `null`:  7.6K -> 18KIE8: without `null`: 14K -> 29.7K with `null`:  14K -> 27K

如我們所料,顯示斷開引用可以釋放內存,但是釋放的內存不是很多,10000個函數對象才釋放大約3M的內存,這對一些小型腳本不算什么,但對于大型程序,或者長時間運行在低內存的設備里的時候,這是非常有必要的。

以上就是關于JScript的Bug與內存管理的全部介紹,希望對大家的學習有所幫助。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 凉山| 明光市| 浮梁县| 乌海市| 诸暨市| 隆化县| 陆河县| 金川县| 壤塘县| 荔波县| 阿勒泰市| 康平县| 丰县| 措美县| 南宁市| 开鲁县| 桃江县| 克什克腾旗| 长岭县| 彝良县| 涿州市| 化州市| 湟源县| 多伦县| 双流县| 正安县| 娱乐| 宜宾市| 旺苍县| 平潭县| 松原市| 东乡| 香格里拉县| 麻城市| 宝应县| 监利县| 芦山县| 西昌市| 沙洋县| 依兰县| 阿巴嘎旗|