[翻译]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时都会产生这样的搜索过程。正是这种搜索过程影响了性能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值