[翻譯]High Performance JavaScript(005)

第二章  Data Access  數據訪問


    One of the classic computer science problems is determining where data should be stored for optimal reading and writing. Where data is stored is related to how quickly it can be retrieved during code execution. This problem in JavaScript is somewhat simplified because of the small number of options for data storage. Similar to other languages, though, where data is stored can greatly affect how quickly it can be accessed later. There are four basic places from which data can be accessed in JavaScript:

    經典計算機科學的一個問題是確定數據應當存放在什麼地方,以實現最佳的讀寫效率。數據存儲在哪裏,關係到代碼運行期間數據被檢索到的速度。在JavaScript中,此問題相對簡單,因爲數據存儲只有少量方式可供選擇。正如其他語言那樣,數據存儲位置關係到訪問速度。在JavaScript中有四種基本的數據訪問位置:


Literal values  直接量
Any value that represents just itself and isn't stored in a particular location. JavaScript can represent strings, numbers, Booleans, objects, arrays, functions, regular expressions, and the special values null and undefined as literals.

直接量僅僅代表自己,而不存儲於特定位置。 JavaScript的直接量包括:字符串,數字,布爾值,對象,數組,函數,正則表達式,具有特殊意義的空值,以及未定義。


Variables  變量
Any developer-defined location for storing data created by using the var keyword.

開發人員使用var關鍵字創建用於存儲數據值。


Array items  數組項
A numerically indexed location within a JavaScript Array object.

具有數字索引,存儲一個JavaScript數組對象。


Object members  對象成員
A string-indexed location within a JavaScript object.

具有字符串索引,存儲一個JavaScript對象。


    Each of these data storage locations has a particular cost associated with reading and writing operations involving the data. In most cases, the performance difference between accessing information from a literal value versus a local variable is trivial. Accessing information from array items and object members is more expensive, though exactly which is more expensive depends heavily on the browser. Figure 2-1 shows the relative speed of accessing 200,000 values from each of these four locations in various browsers.

    每一種數據存儲位置都具有特定的讀寫操作負擔。大多數情況下,對一個直接量和一個局部變量數據訪問的性能差異是微不足道的。訪問數組項和對象成員的代價要高一些,具體高多少,很大程度上依賴於瀏覽器。圖2-1顯示了不同瀏覽器中,分別對這四種數據類型進行200'000次操作所用的時間。


    Older browsers using more traditional JavaScript engines, such as Firefox 3, Internet Explorer, and Safari 3.2, show a much larger amount of time taken to access values versus browsers that use optimizing JavaScript engines. The general trends, however, remain the same across all browsers: literal value and local variable access tend to be faster than array item and object member access. The one exception, Firefox 3, optimized array item access to be much faster. Even so, the general advice is to use literal values and local variables whenever possible and limit use of array items and object members where speed of execution is a concern. To that end, there are several patterns to look for, avoid, and optimize in your code.

    老一些的瀏覽器使用傳統的JavaScript引擎,如Firefox 3,Internet Explorer和Safari 3.2,它們比優化後的JavaScript引擎耗費太多時間。總的來說,直接量和局部變量的訪問速度要快於數組項和對象成員的訪問速度。只有一個例外,Firefox 3,優化過數組項訪問所以非常快。即使如此,一般的建議是,如果關心運行速度,那麼儘量使用直接量和局部變量,限制數組項和對象成員的使用。爲此,有幾種模式來查看、避免並優化你的代碼。

 


Figure 2-1. Time per 200,000 reads from various data locations

圖2-1   對不同數據類型進行200'000次讀操作所用的時間

 

Managing Scope  管理作用域

 

    The concept of scope is key to understanding JavaScript not just from a performance perspective, but also from a functional perspective. Scope has many effects in JavaScript, from determining what variables a function can access to assigning the value of this. There are also performance considerations when dealing with JavaScript scopes, but to understand how speed relates to scope, it's necessary to understand exactly how scope works.

    作用域概念是理解JavaScript的關鍵,不僅從性能的角度,而且從功能的角度。作用域對JavaScript有許多影響,從確定哪些變量可以被函數訪問,到確定this的值。JavaScript作用域也關係到性能,但是要理解速度與作用域的關係,首先要理解作用域的工作原理。

 

Scope Chains and Identifier Resolution  作用域鏈和標識符解析


    Every function in JavaScript is represented as an object—more specifically, as an instance of Function. Function objects have properties just like any other object, and these include both the properties that you can access programmatically and a series of internal properties that are used by the JavaScript engine but are not accessible through code. One of these properties is [[Scope]], as defined by ECMA-262, Third Edition.

    每一個JavaScript函數都被表示爲對象。進一步說,它是一個函數實例。函數對象正如其他對象那樣,擁有你可以編程訪問的屬性,和一系列不能被程序訪問,僅供JavaScript引擎使用的內部屬性。其中一個內部屬性是[[Scope]],由ECMA-262標準第三版定義。

 

    The internal [[Scope]] property contains a collection of objects representing the scope in which the function was created. This collection is called the function's scope chain and it determines the data that a function can access. Each object in the function's scope chain is called a variable object, and each of these contains entries for variables in the form of key-value pairs. When a function is created, its scope chain is populated with objects representing the data that is accessible in the scope in which the function was created. For example, consider the following global function:

    內部[[Scope]]屬性包含一個函數被創建的作用域中對象的集合。此集合被稱爲函數的作用域鏈,它決定哪些數據可由函數訪問。此函數作用域鏈中的每個對象被稱爲一個可變對象,每個可變對象都以“鍵值對”的形式存在。當一個函數創建後,它的作用域鏈被填充以對象,這些對象代表創建此函數的環境中可訪問的數據。例如下面這個全局函數:

function add(num1, num2){
  var sum = num1 + num2;
  return sum;
}

    When the add() function is created, its scope chain is populated with a single variable object: the global object representing all of the variables that are globally defined. This global object contains entries for window, navigator, and document, to name a few. Figure 2-2 shows this relationship (note the global object in this figure shows only a few of the global variables as an example; there are many others).

    當add()函數創建後,它的作用域鏈中填入一個單獨的可變對象,此全局對象代表了所有全局範圍定義的變量。此全局對象包含諸如窗口、瀏覽器和文檔之類的訪問接口。圖2-2指出它們之間的關係(注意:此圖中只畫出全局變量中很少的一部分,其他部分還很多)。

 

Figure 2-2. Scope chain for the add() function

圖2-2  add()函數的作用域鏈

 

    The add function's scope chain is later used when the function is executed. Suppose that the following code is executed:

    add函數的作用域鏈將會在運行時用到。假設運行下面的代碼:
var total = add(5, 10);
    Executing the add function triggers the creation of an internal object called an execution context. An execution context defines the environment in which a function is being executed. Each execution context is unique to one particular execution of the function, and so multiple calls to the same function result in multiple execution contexts being created. The execution context is destroyed once the function has been completely executed.

    運行此add函數時建立一個內部對象,稱作“運行期上下文”。一個運行期上下文定義了一個函數運行時的環境。對函數的每次運行而言,每個運行期上下文都是獨一的,所以多次調用同一個函數就會導致多次創建運行期上下文。當函數執行完畢,運行期上下文就被銷燬。

 

    An execution context has its own scope chain that is used for identifier resolution. When the execution context is created, its scope chain is initialized with the objects contained in the executing function's [[Scope]] property. These values are copied over into the execution context scope chain in the order in which they appear in the function. Once this is complete, a new object called the activation object is created for the execution context. The activation object acts as the variable object for this execution and contains entries for all local variables, named arguments, the arguments collection, and this. This object is then pushed to the front of the scope chain. When the execution context is destroyed, so is the activation object. Figure 2-3 shows the execution context and its scope chain for the previous example code.

    一個運行期上下文有它自己的作用域鏈,用於標識符解析。當運行期上下文被創建時,它的作用域鏈被初始化,連同運行函數的[[Scope]]屬性中所包含的對象。這些值按照它們出現在函數中的順序,被複制到運行期上下文的作用域鏈中。這項工作一旦完成,一個被稱作“激活對象”的新對象就爲運行期上下文創建好了。此激活對象作爲函數執行期的一個可變對象,包含訪問所有局部變量,命名參數,參數集合,和this的接口。然後,此對象被推入作用域鏈的前端。當作用域鏈被銷燬時,激活對象也一同銷燬。圖2-3顯示了前面實例代碼所對應的運行期上下文和它的作用域鏈。

 


Figure 2-3. Scope chain while executing add()

圖2-3  運行add()時的作用域鏈


    Each time a variable is encountered during the function's execution, the process of identifier resolution takes place to determine where to retrieve or store the data. During this process, the execution context's scope chain is searched for an identifier with the same name. The search begins at the front of the scope chain, in the execution function's activation object. If found, the variable with the specified identifier is used; if not, the search continues on to the next object in the scope chain. This process continues until either the identifier is found or there are no more variable objects to search, in which case the identifier is deemed to be undefined. The same approach is taken for each identifier found during the function execution, so in the previous example, this would happen for sum, num1, and num2. It is this search process that affects performance.

    在函數運行過程中,每遇到一個變量,標識符識別過程要決定從哪裏獲得或者存儲數據。此過程搜索運行期上下文的作用域鏈,查找同名的標識符。搜索工作從運行函數的激活目標之作用域鏈的前端開始。如果找到了,那麼就使用這個具有指定標識符的變量;如果沒找到,搜索工作將進入作用域鏈的下一個對象。此過程持續運行,直到標識符被找到,或者沒有更多對象可用於搜索,這種情況下標識符將被認爲是未定義的。函數運行時每個標識符都要經過這樣的搜索過程,例如前面的例子中,函數訪問sum,num1,num2時都會產生這樣的搜索過程。正是這種搜索過程影響了性能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章