动态作用域

无论是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); //true
alert(book1 instanceof Object); //true
book1.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.一个属性或方法在原形链中的位置越深,访问速度就越慢。

高性能的JavaScript--数据访问(2)的更多相关文章

  1. JavaScript 数据访问(通译自High Performance Javascript 第二章) [转]

    JavaScript 数据访问(通译自High Performance Javascript 第二章)   JavaScript 数据访问(翻译自High Performance Javascript ...

  2. Javascript高性能编程-提高数据访问速度

         hasOwnProperty()仅检索实例不检索原型,in即检索实例,又检索原型      成员嵌套越深,访问速度越慢,只在必要的情况下使用对象成员.      如果在同一个函数中你要多次读 ...

  3. 高性能js之数据访问性能

    js中si中基本数据访问: 直接量, 变量, 数组项, 对象成员 性能问题: 首先要理解作用域链的基本概念,例如,当一个函数被创建时,就会产生一个激活对象(AO对象),AO对象中存储了该函数中所有的属 ...

  4. 高性能JavaScript笔记一(加载和执行、数据访问、DOM编程)

    写在前面 好的书,可能你第一遍并不能领会里面的精魂,当再次细细品评的时候,发现领悟的又是一层新的含义 (这段时间,工作上也不会像从前一样做起来毫不费力,开始有了新的挑战,现在的老大让我既佩服又嫉妒,但 ...

  5. 高性能Javascript--高效的数据访问

    接上一篇,希望能写一个高性能Javascript专题. 第一篇:高性能Javascript--脚本的无阻塞加载策略. 参考摘录<高性能Javascript>. 经典计算机科学的一个问题是, ...

  6. 分享自己的超轻量级高性能ORM数据访问框架Deft

    Deft 简介 Deft是一个超轻量级高性能O/R mapping数据访问框架,简单易用,几分钟即可上手. Deft包含如下但不限于此的特点: 1.按照Transact-SQL的语法语义风格来设计,只 ...

  7. 超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上

    超轻量级高性能ORM数据访问组件Deft,比dapper快20%以上 阅读目录 Deft简介 Deft 核心类介绍 Deft 3分钟即可上手使用 其他可选的配置参数 性能测试 Demo代码下载 回到顶 ...

  8. 高性能的JavaScript -- 读书笔记

    高性能的JavaScript 一.      加载和运行 将脚本放在底部 脚本下载解析执行时,页面已经加载完成并显示在用户面前 成组脚本 减少外部脚本文件数量,整合成一个文件 延迟脚本 动态脚本元素 ...

  9. ClownFish:比手写代码还快的通用数据访问层

    http://www.cnblogs.com/fish-li/archive/2012/07/17/ClownFish.html 阅读目录 开始 ClownFish是什么? 比手写代码还快的执行速度 ...

  10. Dojo Data Store——统一数据访问接口

    原文地址:http://www.infoq.com/cn/articles/wq-dojo-data-store 无论在传统的桌面应用还是在主流的互联网应用中,数据始终占据着软件应用中的核心地位.当下 ...

随机推荐

  1. Linux启动界面切换:图形界面-字符界面(转)

    Linux字符界面切换到图形界面 由字符界面切换到图形界面可用两种简单方法实现: 1.在字符界面输入startx或init 5 . 2.通过编辑/etc/inittab文件实现默认进入图形界面. 把其 ...

  2. HTML Select 标签选择后触发jQuery事件代码实例

    页面设计原由: 因为很多客户不知道如何来到我们公司,领导想让我在微信公众号上面做一个链接,客户可以直接通过微信公众号打开地图并导航到我们公司的办公地点. 实现起来并不难,但由于公司有很多办事处,所以需 ...

  3. 忘记mysql root 密码修改小技巧

    首先我说一下我的情况,我并不是忘记了我的root密码,只不过是我在使用phpmyadmin的时候更改密码的时候选择了如图1 的这个方法将密码加密并更改了,然后就再次登录的时候登录不上,所以对于菜鸟级的 ...

  4. Qt控件样式 Style Sheet Demo

    迟来的笔记,作为一个程序员每日记事已养成习惯,离开许久,不知不觉已喜欢用文字表达对技术的热爱,学无止境! Qt – 一个跨平台应用程序和UI开发框架:它包括跨平台类库.集成开发工具和跨平台 IDE,使 ...

  5. html5 第一天

    html4与html5的琐碎比较,不全,第一次写,望多多包涵. 一 兼容性:html5在老版本的浏览器上也可以运行 二 实用性:HYML5都是封装的简单使用功能 三非革命性的发展 Html5向前兼容, ...

  6. javascript学习笔记(2)————this

    //简单的学习JavaScript中this关键词 //this在于我简单的理解就是谁调用了当前方法(函数),this就指向谁 var a = 20; function fn1(){ this.a = ...

  7. Python 网络爬虫(图片采集脚本)

    ===============爬虫原理================== 通过Python访问网站,获取网站的HTML代码,通过正则表达式获取特定的img标签中src的图片地址. 之后再访问图片地址 ...

  8. Zookeeper开源客户端框架Curator简介

    Curator是Netflix开源的一套ZooKeeper客户端框架. Netflix在使用ZooKeeper的过程中发现ZooKeeper自带的客户端太底层, 应用方在使用的时候需要自己处理很多事情 ...

  9. 不得不说的wepapi 优化

    1:在你的ASP.NET Web API中使用GZIP 或 Deflate .对于减少响应包的大小和响应速度,压缩是一种简单而有效的方式.这是一个非常有必要使用的功能,你可以查看更多关于压缩的文章在我 ...

  10. bug 汇总

    联通营业厅充话费无法在线支付,chrome Python 64位安装包 win7 64 windows 10 右键菜单 Android studio IE11 layout