[转载]向高级Javascript程序员阵营迈进:Javascript一些概念研究总结

简介:


习惯于OOP语言编程后,会发现Javascript世界有很多匪夷所思的奇奇怪怪的现象(比如闭包),我花了大量的精力研究这些奇怪现象的根源,最后发现:源自于javascript的作用域不是块级作用域,同时它有一套基于作用域链的标识查找机制。本文大部分内容来自互联网,经过整理、改进而成。

  • Javascript引擎和DOM采用的垃圾回收算法:引用计数
    javascript和DOM有各自的垃圾回收器,单独运作良好,合作时一不小心会出问题。引用计数这个算法的缺陷就是:Javascript 对象和DOM对象彼此循环引用,造成彼此的引用计数永远不能为0,垃圾回收器无法正确回收这些参与循环引用的对象,最终造成内存泄漏(Memory Leak)。闭包是循环引用“大户”。如果对垃圾回收感兴趣,可以看看 垃圾收集趣史
  • 词法作用域(lexical scope,一般简称作用域)、with/eval
    简单来说javascript的作用域是由function划分的。读完这篇文章你会了解词法作用域Javascript运行机制浅探,with/eval这 两个特例会扰乱作用域,即所谓动态作用域(dynamic scope)
  • 作用域链(Scope Chain) 和 标识查找机制
    作用域链是一个链表(数据结构),它是Javascript的灵魂,只有理解了它才能理解Javascript世界奇奇怪怪的现象。作用域链由活动对象链成。
    标识查找机制稍后结合函数执行的原理加以说明。
  • 活动对象(call object)
    国内很多人称之为调用对象(call object),本文用英文call obejct(但我私下认为翻译为"活动对象"更好,不至于和this所指的对象混淆。)
    非常特殊的javascript引擎内的对象,ECMAScript规范术语称之为activation object(活动对象)。多个call object和全局对象组成作用域链(scope chain )
  • 函数的本质(有名函数、匿名函数)、函数的[[scope]]属性   函数在javascript里面是一个特殊的引用类型 ,它继承于位于javascript世界最顶端的object,类型是Function,是其他常见引用类型的构造函数的所属类型。
    在定义函数的时候,Javascript引擎会为function对象的一个私有[[scope]]属性赋值,理论上只有js引擎自己才能访问(也即:一般情况下无法通过语法来访问,但Firefox下有一个__parent___可以访问到)。匿名函数的[[scope]]属性指向匿名函数定义时的上下文对象;有名函数除了和匿名函数一样,还会在[[scope]]属性的顶端再指向一个Javascript对象(继承自obejct.prototype),这个对象被链接到函数定义时的Scope Chain,他本身带有一个属性就是函数的名字,这确保函数内部的代码可以无误地访问到自己的函数名以便进行递归。
    当定义函数的时候,javascript解析器会将函数的作用域链(scope chain)设置为定义函数时函数所在的“环境”,如果函数是一个全局函数,则scope chain中只有window对象。
    当执行函数时的微观世界,请看稍后的说明。
  • 闭包(closure)
    javascript所有的函数都是闭包,但是只有嵌套形式的闭包(也是我们经常讨论的形式)才能体现这个javascript 特性的强大。推荐阅读这篇文章: 
    深入理解JavaScript闭包(closure)
  • 函数执行时的作用域链和活动对象是如何形成的及与闭包的关系
    1、javascript解析器启动时就会初始化建立一个全局对象global object,这个全局对象就 拥有了一些预定义的全局变量和全局方法,如Infinity, parseInt, Math,所有程序中定义的全局变量都是这个全局对象的属性。在客户端javascript中,Window就是这个javascript的全局对象。

    2、当javascript执行一个function时,会生成一个对象,称之为call object,function中的局部变量和function的参数都成为这个call object的属性,以免覆写同名的全局变量。

    3、javascript解析器每次执行function时,都会为此function创建一个execution context执行环境,在此function执行环境中最重要的一点就是function的作用域链scope chain,这是一个对象链,由全局对象和活动对象构成,对象链具体构成过程见下面说明。

    4、标识的查找机制:当javascript查询变量x的值时,就会检查此作用域链中第一个对象,可能是function的call object或全局对象(比如window),如果对象中有定义此x属性,则返回值,不然检查作用域链中的下一个对象是否定义x属性,在作用域链中没有找到,最后返回undefined。

    5、当javascript执行一个function时,它会先将此function定义时的作用域作为其作用域链,然后创建一个活动对象(call object),置于作用域链的顶部,function的参数及内部var声明的所有局部变量都会成为此调用对象的属性。

    6、this关键词指向方法的调用者,而不是以调用对象的属性存在,同一个方法中的this在不同的function调用中,可能指向不同的对象。

    7、The Call Object as a Namespace。将活动对象当作命名空间使用,避免命名污染。
    (function() {
    // 在方法体内用var声明的所有局部变量,都是以方法调用时创建的活对象的属性形式 存在。
    // 这样就避免与全局变量发生命名冲突。
    })();

    8、javascript中所有的function都是一个闭包,但只有当一个嵌套函数被导出到它所定义的作用域外时,这种闭包才强大。如果理解了闭包,就会理解function执行时的作用域链和活动对象,才能真正掌握javascript。

    9、嵌套闭包的微观世界:在嵌套闭包时,当内部函数的引用被保存到嵌套闭包之外一个全局变量或者一个对象的属性时,在这种情况下,此内部函数有一个外部引用,并且在其外围调用函数的活动对象中有一个属性指向此内部函数。因为有其他对象引用此内部函数,所以在外围函数被调用一次后,其创建的活动对象会继续存在,并不会被垃圾回收器回收(因为引用计数不为0),内部函数的参数和局部变量都会在这个活动对象中得以维持,javascript代码任何形式都不能直接访问此活动对象,但是此活动对象是内部函数被调用时创建的作用域链的一部分,可以被内部函数访问并修改。

最后介绍一个奇怪现象:下面的代码,为什么鼠标移动到li上,title总是6,而不是我们所预想的数字呢?看你能不能根据以上的知识,解释这种现象的原因。提示:变量查找机制。

< html >  
< head >  
< title > 循环内的闭包 应该谨慎 </ title >  
</ head >  
< body >  
< ul  id ="list" >  
< li > 第1条记录 </ li >  
< li > 第2条记录 </ li >  
< li > 第3条记录 </ li >  
< li > 第4条记录 </ li >  
< li > 第5条记录 </ li >  
< li > 第6条记录 </ li >  
</ ul >  
< script  type ="text/javascript" >
var  list_obj  =  document.getElementById( " list " ).getElementsByTagName( " li " );  // 获取list下面的所有li的对象数组
for  ( var  i  =  0 ; i  <=  list_obj.length; i ++ ) {
list_obj[i].onmousemove 
=  function () {
this .style.backgroundColor  =  " #eee " ;
document.title
= i
};
list_obj[i].onmouseout 
=  function () {
this .style.backgroundColor  =  " #fff " ;
}
}
</ script >  
</ body >  
</ html >

为什么上面的代码改成下面的就好了呢?

< html >  
< head >  
< title > 循环内的闭包 应该谨慎 </ title >  
</ head >  
< body >  
< ul  id ="list" >  
< li > 第1条记录 </ li >  
< li > 第2条记录 </ li >  
< li > 第3条记录 </ li >  
< li > 第4条记录 </ li >  
< li > 第5条记录 </ li >  
< li > 第6条记录 </ li >  
</ ul >  
< script  type ="text/javascript" >
var  list_obj  =  document.getElementById( " list " ).getElementsByTagName( " li " );  // 获取list下面的所有li的对象数组
for  ( var  i  =   0 ; i  <=  list_obj.length; i ++ ) {
(
function (i){
list_obj[i].onmousemove 
=   function () {
this .style.backgroundColor  =   " #eee " ;
document.title
= i
};
list_obj[i].onmouseout 
=   function () {
this .style.backgroundColor  =   " #fff " ;
}
})(i);
}
</ script >  
</ body >  
</ html >

 

 文章转载自:http://www.cnblogs.com/kaima/archive/2009/03/07/advanced_javascript_concept.html

 

本文转自 酷小孩 博客园博客,原文链接: http://www.cnblogs.com/babycool/archive/2012/03/25/2416460.html ,如需转载请自行联系原作者

版权
作者: 酷小孩 
相关文章
|
1月前
|
机器学习/深度学习 自然语言处理 JavaScript
信息论、机器学习的核心概念:熵、KL散度、JS散度和Renyi散度的深度解析及应用
在信息论、机器学习和统计学领域中,KL散度(Kullback-Leibler散度)是量化概率分布差异的关键概念。本文深入探讨了KL散度及其相关概念,包括Jensen-Shannon散度和Renyi散度。KL散度用于衡量两个概率分布之间的差异,而Jensen-Shannon散度则提供了一种对称的度量方式。Renyi散度通过可调参数α,提供了更灵活的散度度量。这些概念不仅在理论研究中至关重要,在实际应用中也广泛用于数据压缩、变分自编码器、强化学习等领域。通过分析电子商务中的数据漂移实例,展示了这些散度指标在捕捉数据分布变化方面的独特优势,为企业提供了数据驱动的决策支持。
69 2
信息论、机器学习的核心概念:熵、KL散度、JS散度和Renyi散度的深度解析及应用
|
1月前
|
设计模式 JavaScript 前端开发
探索JavaScript中的闭包:从基础概念到实际应用
在本文中,我们将深入探讨JavaScript中的一个重要概念——闭包。闭包是一种强大的编程工具,它允许函数记住并访问其所在作用域的变量,即使该函数在其作用域之外被调用。通过详细解析闭包的定义、创建方法以及实际应用场景,本文旨在帮助读者不仅理解闭包的理论概念,还能在实际开发中灵活运用这一技巧。
|
1月前
|
存储 JavaScript 前端开发
JavaScript 对象的概念
JavaScript 对象的概念
37 4
|
1月前
|
缓存 JavaScript 前端开发
深入了解JavaScript的闭包:概念与应用
【10月更文挑战第8天】深入了解JavaScript的闭包:概念与应用
|
1月前
|
前端开发 JavaScript 程序员
【从前端入门到全栈】Node.js 之核心概念
【从前端入门到全栈】Node.js 之核心概念
|
1月前
|
自然语言处理 JavaScript 前端开发
深入理解JavaScript中的闭包:概念与应用
【10月更文挑战第8天】深入理解JavaScript中的闭包:概念与应用
|
2月前
|
自然语言处理 JavaScript 前端开发
探索JavaScript中的闭包:从基础概念到实际应用
本文深入探讨了JavaScript中闭包的概念,从定义、作用域链和实际应用等方面进行了详细阐述。通过生动的比喻和实例代码,帮助读者理解闭包在函数执行上下文中的重要性,以及如何在实际开发中有效利用闭包解决复杂问题。同时,文章也指出了过度使用闭包可能导致的潜在问题,并给出了相应的优化建议。
|
3月前
|
JavaScript 前端开发
js bom的概念
js bom的概念
35 1
|
3月前
|
JavaScript 前端开发
JavaScript BOM 的概念(浏览器对象模型)
JavaScript BOM 的概念(浏览器对象模型)
49 1
|
3月前
|
JavaScript 前端开发
JavaScript 事件的概念
JavaScript 事件的概念
60 1