javascript笔记:通过对作用域链和执行环境的深入理解所得出的提高javascript代码性能的建议

  上篇文章里我结束了对象的创建的内容,最后引出了作用域链和执行环境的问题。当我对这块知识有了更深入的了解后,回头看看jQuery源码才知道大师们写的代码是如何的厉害,jQuery源码里很好的运用了作用域链和执行环境的知识来提升程序性能。

  好了,不废话了,上篇博文里对作用域讲的比较简略。其实对作用域的理解是理解整个javascript语言的关键所在,特别我在写javascript笔记时候曾对很多怪异的javascript用法无法理解的透彻,究其原因还是没有真正理解javascript里作用域的概念。

  Javascript里的作用域到底决定了什么呢?作用域决定了那些变量能被函数所访问(注意:作用域是函数的内部属性,谈到作用域是绕不开的function),作用域也确定了this指针的指向。上篇博文里我说道,程序其实就是不断检索数据的过程,那么检索数据的效率决定了程序的性能,因此作用域既然决定那些变量能被访问,当然也决定了检索这些变量的方式,所以想写出高效的javascript程序灵活运用作用域的原理是关键了。

  上篇博文里面,我写了一个函数,代码如下:

复制代码
<script type="text/javascript">
function add(a,b)
{
    var sum = a + b;
    return sum;
}
</script>
复制代码

  大家可以看到这个函数属于window而非function,在页面被加载时候,add函数会被初始化,生成属于自己的作用域链,上篇文章里我在firebug里设定断点调试贴出了一个我截图下来的变量图,认为这就是函数add的Scope所包含的变量,现在发现当时理解是错误的,那些变量是window的“全局环境”的变量图而非是add函数的内部属性Scope的变量图。在此我纠正一下。

  下面我要着重讲讲作用域链和执行环境,这块知识我在javascript对象创建的中篇里提到过,这里我将那些知识回顾下:

复制代码
    什么是执行环境呢?在javascript里面执行环境分为两类,一类是全局环境,一类是局部环境,整个页面里被共享的方法和属性就是在全局环境,相对于全局环境,函数{}号里的执行环境就是局部环境,执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为,每个执行环境都定义了一个与之相关的变量对象,环境中定义的所有变量和函数都保存在这个对象里,虽然我们自己编写的代码无法访问这个对象,但解析器在处理数据时候后台会使用到它。
    全局执行环境另一种说法是最外围的一个执行环境,在web浏览器的范围中(actionscript也是施行了ECMAScript标准,它的全局范围就和javascript的全局范围不同),全局执行环境被认为是window对象,因此全局变量和函数都是作为window对象的方法和属性来创建的,全局执行环境知道应用程序退出比如关闭网页或浏览器才会被销毁。而局部环境则是以函数对象作为关联对象。
复制代码

  大家要注意环境的前缀是:执行,也就说只有函数被执行(局部执行环境)和页面被加载(全局执行环境),下面就是页面被加载时候的全局执行环境变量:

  现在我们要执行add函数了,代码如下:

复制代码
function add(a,b)
{
    var sum = a + b;
    return sum;
}

console.log(add(10,20));
复制代码

  

  这是add函数的执行环境的变量图是:

  1.firebug:

  2.chrome的代码调试器:

  代码的结构图如下:

  在函数被执行时候,函数会创建一个“运行期上下文(execution context)”的内部对象,这个运行期上下文在我的理解里就是函数的执行环境,每个运行期上下文都有自己的作用域链,用于标识符解析。

  当运行期上下文被创建时候,他的作用域链初始化为当前运行函数【Scope】属性中所含的对象(见firebug和chrome调试器里的内存图,比如add函数除了a,b,sum,this还有arguments,不过这个在firebug和chrome调试器里看不到的)。这些值按照它们出现在函数中的顺序,被复制到执行期上下文的作用域链中。这个过程一旦完成,一个被称为“活动对象(activation object)”的新对象就为执行期上下文创建好了,活动对象作为函数运行期的可变对象,包含了所有局部变量,命名参数,参数集合以及this。然后此对象被推入到作用域链的前端。当运行期上下文被销毁的时候,活动对象也随之被销毁。在函数执行过程中,每遇到一个变量都会进行一次标识符的解析,这种解析决定了我们从啥地方获取变量或者将变量存储到什么地方,这个过程是搜索整个函数运行期上下文的作用域链,查找同名的标识符,搜索过程都是从作用域头部开始,也就是当前运行函数的活动对象。找到了就使用它,没有找到则会继续搜索作用域链中的下一个对象,搜索过程会延续到找到标识符为止或者是没有找到为止,这种情况就是标识符没有被定义了

  在javascript里使用变量就是在做标识符解析,由上面的解释我们知道标识符的解析一定是有计算机性能的消耗的,当标识符位于执行上下文作用域链的位置越深,性能也就越慢了,那么到底什么地方最慢了?比如我们举例的函数add,当我们搜索到global全局变量总会比函数内部的局部变量慢,假如我们定义个更复杂的函数里面有多层嵌套的话,访问全局变量就是性能的梦魇了。因此在函数中我们尽量多使用函数内部的局部变量。(由于浏览器的产品的差异,个别浏览器的访问变量的性能可能会有差异,但是总体而言,访问函数局部变量永远是最快的)。

  尽量使用局部变量,就带来一个十分重要的提高程序性能的用法:我们在函数内部使用全局变量可以说是一种跨作用域操作,如果某个跨作用域的值在函数的内部被使用到一次以上,那么我们就把它存储到局部变量里

  代码书写的格式就是:

function ftn()
{
    var doc = document;
    .......
}

 

  将全局的变量用var定义到局部变量里。

  这个用法在jQuery源码里一开头就清晰可以到看:

复制代码
var jQuery = function( selector, context ) {
        // The jQuery object is actually just the init constructor 'enhanced'
        return new jQuery.fn.init( selector, context );
    },

    // Map over jQuery in case of overwrite
    _jQuery = window.jQuery,

    // Map over the $ in case of overwrite
    _$ = window.$,

    // Use the correct document accordingly with window argument (sandbox)
    document = window.document,
复制代码

  jQuery把经常使用的全局变量都存储在函数的局部变量里。


jquery中文汉化版 (function( window, undefined ) { //不要做这个因为各自的应用程序包括ASP.NET查找 // the stack via arguments.caller.callee and Firefox dies if //你尝试查找通过“精确使用”呼叫链接(#13335) //支持:火狐浏览器 18+ //“精确使用”; var //deferred对象被使用在DOM(Document Object Model翻译:文档对象模型)准备之时 //deferred(延迟)对象:从jQuery 1.5.0版本开始引入的一个新功能 //在DOM准备好时调用 readyList, //一个中心引用对于jQuery根文档 //对根jQuery对象的主要引用 rootjQuery, //支持:IE9之前的版本 // For `typeof node.method` instead of `node.method !== undefined` core_strundefined = typeof undefined, // Use the correct document accordingly with window argument (sandbox) document = window.document,//window文档赋值给变量document location = [removed], // Map over jQuery in case of overwrite(不确定,待修正,希望高人帮忙翻译一下) //在jQuery上绘制写在上面的实例 //防止被覆盖 _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$, //将window正则表达式符号$赋值给变量_$ //[类]:成双类型 class2type = {}, //在贮存区被删除数据ID的列表,我们能够再用他们 core_deletedIds = [], core_version = "1.9.1", //保存一个参考给一些核心的方法 //为核心方法创建引用 core_concat = core_deletedIds.concat, core_push = core_deletedIds.push, core_slice = core_deletedIds.slice, core_indexOf = core_deletedIds.indexOf, core_toString = class2type.toString, core_hasOwn = class2type.hasOwnProperty, core_trim = core_version.trim, //规定一个jQuery本地代码 //构建jQuery对象 jQuery = function( selector, context ) { //jQuery对象是实际上初始化名为enhanced(提高的)构造器 //jQuery对象实际上只是增强的初始化构造方法 return new jQuery.fn.init( selector, context, rootjQuery ); }, /* 用来匹配数字的正则,匹配可选正负号、浮点型、整型、科学计数法 * 没有使用(?)来表示可选而是通过(|)来选择 * (?:\d*\.|)匹配浮点数时,|前的\d*\.可以匹配整数部分和小数点,小数部分由后面的\d+匹配 * 匹配整数时,|)可以保证匹配继续向下进行,整数由后面的\d+匹配,同样的\d+在匹配整型和浮点型时负责的匹配部分不同 * [eE][\-+]?\d+|)处理科学计数法的匹配,同样没有使用?表示可选 */ core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, //用于分开空格 core_rnotwhite = /\S+/g, //查找非空白字符串 // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE) rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, //\uFEFF:字节顺序标志 //一个简单途径用于检查HTML字符串 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) // Strict HTML recognition (#11290: must start with <) rquickExpr = /^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/, //匹配一个独立的标签 rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, // JSON RegExp(JavaScript Object Notation:JavaScript对象标记法正则表达式) rvalidchars = /^[\],:{}\s]*$/, rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, rdashAlpha = /-([\da-z])/gi, //以上为正则运算表达式各种形式,不太容易理解,尽量掌握。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值