動態範圍無論是with運算式還是try-catch運算式的catch子句,以及包含()的函數,都被認為是動態範圍。一個動態範圍只因為代碼運行而存在。因此無法通過靜態分析(查看代碼機構)來確定(是否存在範圍)。例如: 複製代碼function execute(code) {(code);function subroutine(){return window;}var w = subroutine();//what value is w?};複製代碼execute()函數看上去像一個動態範圍,因為它使用了()。w變數的值與code有關。大多數情況下,w將等價於全域變數window對象,但是請考慮如下情況: execute("var window = {};")這種情況下,()在execute()函數中建立了一個局部的window變數,所以這個w將等價於這個局部的window變數而不是全域的那個。所以說,不運行這段代碼是沒有辦法瞭解具體情況的,標識符window的確切含義不能預先確定。 閉包,範圍,和記憶體 閉包是JavaScript最強大的一個方面,它允許函數訪問局部範圍之外的資料。閉包的使用在當今最複雜的網頁應用中無處不在,不過,有一種效能影響與閉包有關。 為瞭解閉包的有關效能問題,考慮下面的例子: function assignEvents(){var id = "xdi9592";document.getElementById("save-btn").onclick = function(event){saveDocument(id);};}assignEvents()函數為一個dom元素制定了一個事件處理控制代碼,此事件處理控制代碼是一個閉包,當assignEvents()執行時建立,可以訪問其範圍內部的id變數,用這種方法封閉對id變數的訪問,必須建立一個特定的範圍鏈。 當assignEvents()被執行時,一個啟用物件被建立,並包含了一些應有的內容,其中包括id變數。它將成為運行期上下文範圍鏈上的第一個對象,全域對象是第二個。當閉包建立時,[[Scope]]屬性與這些對象一起被初始化。 由於閉包的[[Scope]]屬性包含與運行期上下文範圍鏈相同的對象引用,會產生副作用。通常,一個函數的啟用物件與運行期上下文一同銷毀。當涉及閉包時,啟用物件就無法銷毀了,因為引用任然存在於閉包的[[Scope]]屬性中,這意味著指令碼中的閉包與非閉包函數相比,需要更多的記憶體開銷。在大型網頁應用中,這可能是個問題,尤其在IE中更被關注。IE使用非本地JavaScript對象實現DOM對象,閉包可能導致記憶體泄露。 當閉包被執行時,一個運行期上下文將被建立,它的範圍鏈與[[Scope]]中引用的兩個相同的範圍同時被初始化,然後一個新的啟用物件為閉包自身被建立。 主要閉包中使用的兩個標識符,id和saveDocument,存在於範圍鏈第一個對象之後的位置上。這是閉包最主要的效能關注點:你經常訪問一些範圍之外的標識符每次訪問都導致一些效能損失。 在指令碼中最好是小心地使用閉包,記憶體和運行速度都值得被關注。將常用的域外變數存入局部變數中,然後直接存取局部變數。 對象成員 大多數JavaScript代碼以物件導向的形式編寫。無論通過建立自訂對象還是使用內建對象,諸如文件物件模型(DOM)和瀏覽器物件模型(BOM)之中的對象。因此,存在很多個物件成員訪問。 對象成員包括屬性和方法,在JavaScript中,二者差別甚微。對象的一個命名成員可以包括任何資料類型。既然函數也是一種對象,那麼對象成員除了傳統的資料類型外,也可以包含一個函數。當一個成員用了一個函數時,它被稱作一個“方法”,而一個非函數類型的資料則被稱作“屬性”。 原形 對象成員比直接量或局部變數訪問速度慢,在某些瀏覽器上比訪問數組項還要慢。這和JavaScript中對象的性質有關。 JavaScript中的對象是基於原形的,原形是其他對象的基礎,定義並實現了一個新對象所必須具有的成員。這一概念完全不同於傳統物件導向編程中“類”的概念,它定義了建立新對象的進程。原形對象為給定類型的對象執行個體所共用,因此所有執行個體共用原型對象的成員。 一個對象通過一個內部屬性綁定到它的原形。Firefox ,Safari和Chrome向開發人員開放這一屬性,稱作__proto__,其他瀏覽器貌似不允許指令碼訪問這一屬性。任何時候你建立一個內建類型的執行個體,如object或者Arrary,這些執行個體自動擁有一個Object作為他們的原形。 因此,對象可以有兩種類型的成員:執行個體成員(“own”成員)和原形成員。執行個體成員直接存在於執行個體自身,而原形成員則從對象原形繼承。如: var book = {title: "High Performance JavaScript",publisher: "Yahoo! Press"};alert(book.toString()); //"[object Object]"如 book對象有兩個執行個體成員:title 和publisher,注意它並沒有定義tostring()介面,但是這個介面卻被調用了,也沒用拋出錯誤。toString()函數就是一個book對象繼承的原形成員。 原形鏈 對象的原形決定了一個執行個體的類型。預設情況下,所有對象都是Object 的執行個體,並繼承了所有基本方法。如toString()。也可以用“構造器”建立另外一種類型的原形。 複製代碼function Book(title, publisher){this.title = title;this.publisher = publisher;}Book.prototype.sayTitle = function(){alert(this.title);};var book1 = new Book("High Performance JavaScript", "Yahoo! Press");var book2 = new Book("JavaScript: The Good Parts", "Yahoo! Press");alert(book1 instanceof Book); //truealert(book1 instanceof Object); //truebook1.sayTitle(); //"High Performance JavaScript"alert(book1. toString()); //"[object Object]"複製代碼Book構造器用於建立一個新的Book執行個體。book1的原形(__proto__)是Book.prototype,Book.prototype的原形是Object。這就建立了一個原形鏈,book1和book2繼承了他們的成員。主要的是,兩個Book執行個體共用同一個原形鏈。每個執行個體擁有自己的title和publisher屬性,但是其他成員均繼承自原形。當book1.toString()被調用時,搜尋工作必須深入原形鏈才能找到對象成員"toString",深入原形鏈越深,搜尋速度就越慢。每深入原形鏈一層都會增加效能損失。搜尋執行個體成員的過程比訪問直接量或者局部量負擔更重,所以增加遍曆原形鏈的開銷正好放大了這種效果。 嵌套成員由於對象成員可能包含其他成員,例如不太常見的寫法window.location.href這種模式。每遇到一個點號,JavaScript引擎就要在對象成員上執行一次解析過程。成員嵌套越深,訪問速度越慢。location.href總是快於window.location.href,而後者也要比window.location.href.toString()更快。如果這些屬性不是對象的執行個體屬性,那麼成員解析還要在每個點上索搜原形鏈,這將需要更長時間。 緩衝對象成員的值由於所有這些效能問題與對象成員有關,所以如果可能的話就避免使用他們。更確切的說,只有在必要情況下使用對象成員。例如沒有理由在一個函數中多次讀取同一個對象成員的值: function hasEitherClass(element, className1, className2){return element.className == className1 || element.className == className2;}element.className被訪問了兩次,我們可以存入一個局部變數,消除一次搜尋過程: function hasEitherClass(element, className1, className2){var currentClassName = element.className;return currentClassName == className1 || currentClassName == className2;}一般來說,如果在同一函數中你要多次讀取同一個對象屬性,最好將它存入到一個局部變數。以局部變數替代屬性,避免多餘的屬性尋找帶來的效能開銷。在處理嵌套對象成員時這點特別重要,他們會對運行速度產生難以置信的影響。 總結1.在JavaScript中,資料存儲存的位置可以對代碼整體效能產生重要影響。有4種資料類訪問類型:直接變數,變數,數組項,對象成員。他們有不同的效能考慮。 2.直接變數和局部變數訪問速度非常快,數組項和對象成員需要更長時間。 3.局部變數比域變數快,因為它位於範圍鏈的第一個對象中。變數在範圍鏈中的位置越深訪問所需的時間就越長。全域變數總是最慢的,因為它們總是位於範圍鏈的最後一環。 4.避免使用with運算式,因為它改變了運行期內容相關的範圍鏈。而且應當小心對待try-catch運算式catch子句,因為它具有同樣的效應。 5.嵌套對象成員會造成重大效能影響,盡量少用。 6.一個屬性或方法在原形鏈中的位置越深,訪問速度就越慢。