Javascript的对象作用域

Javascript的对象作用域
1 vote, 4.00 avg. rating (80% score)

对象的作用域

在Javascript中,一切皆是对象,包括函数。其作用域的概念和其它语言都差不多,都是指一个对象的有效引用范围。在每次调用一个函数的时候 ,就会进入一个函数内的作用域,当从函数返回以后,就返回调用前的作用域。

多数语言都是用堆栈方式来实现作用域的,如C/C++,但Javascript使用的作用域是列表形式的,是链式的,其有名的原型链可以说就是作用域链(scope chain)的一种。参考Ecma-262文档的描述:

  1. 任何执行上下文时刻的作用域, 都是由作用域链来实现。

  2. 在一个函数被定义的时候, 会将它定义时刻的scope chain链接到这个函数对象的[[scope]]属性。

  3. 在一个函数对象被调用的时候,会创建一个活动对象, 然后对于每一个函数的形参,都命名为该活动对象的命名属性, 然后将这个活动对象做为此时的作用域链(scope chain)最前端, 并将这个函数对象的[[scope]]加入到scope chain中。

可以参看Javascript作用域原理这篇文章进行更深入的理解,这里就不进行详述了。这里只对其中一个容易被大家忽视的一个特性进行说明。

JS6743威指南中有一句很精辟的描述: “JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。

with关键字

with 语句用于设置代码在特定对象中的作用域。

它的语法:with (expression) statement

例如:

在这个例子中,with 语句用于字符串,所以在调用 toUpperCase() 方法时,解释程序将检查该方法是否是本地函数。如果不是,它将检查伪对象 sMessage,看它是否为该对象的方法。然后,alert 输出 “HELLO”,因为解释程序找到了字符串 “hello” 的 toUpperCase() 方法。

提示:with 语句是运行缓慢的代码块,尤其是在已设置了属性值时。大多数情况下,如果可能,最好避免使用它。

变量还是属性?

关于delete可以看看这篇文章深入详解javascript之delete操作符英文原文),这里我就不多说了,但需要明确的一点是delete只能删除属性而不能删除变量(能否被删除需要具体看该属性的内部属性中是否具有可删除标识)!在明确了这一点,加上这个例子的印证,相信你会知道,a是变量,而b却是一个属性。

问题是,为什么a是变量,而b不是?b又是谁的属性?

结合前面的作用域链和Javascript预编译的知识:在预编译器执行函数定义语句的时候,会创建一个这个函数对象的内部属性[[scope]],当函数定义在全局范围时,此时的[[scope]]只是指向全局活动对象window。在调用执行函数的时候,JS引擎会创建一个活动对象(执行这个函数的上下文对象),然后再这个活动对象上创建arguments属性,然后再创建与函数入参同名的属性;对于每一个在这个函数中申明的局部变量和函数定义, 都作为该活动对象的同名命名属性。然后将调用参数赋值给形参数,对于缺少的调用参数,赋值为undefined。最后将这个活动对象做为作用域链的最前端,并将函数的[[scope]]属性所指向的,定义函数时候的顶级活动对象, 加入到作用域链。

注意:函数对象的[[scope]]属性是在定义一个函数的时候决定的,而非调用的时候。

有了上面的作用域链,在发生标识符解析的时候,就会逆向查询当前作用域列表的每一个活动对象的属性,如果找到同名的就返回。找不到,那就是这个标识符没有被定义。

全局代码相当于是放在了一个全局的匿名函数中,相当于C/C++的main函数(不完全是),JS引擎首先会去执行这个函数,这个过程同上面所说的过程是一样的,还有就是这个顶级活动对象是window(默认)。

回到这个例子中,在预编的时候,只会在活动对象window中创建变量a的同名属性(显示声明定义的语句会被JS引擎预编译成活动对象的属性);然后执行到b的赋值语句的时候,JS引擎会去在作用域链上进行搜索,由于没有搜索到b,于是在活动对象window上创建同名属性b并初始化赋值。相信到这里你已经明白了,b只是window对象的一个属性而已。

这里在补充下下面的总结(来自深入详解javascript之delete操作符英文原文)这篇文章):

  • 变量和函数的声明实际上都会成为全局对象或者当前函数活动对象的属性。
  • 属性都有一个DontDelete标记,用于表明该属性是否能被delete。
  • 变量和函数的声明创建的属性都会带有DontDelete标记。
  • 函数内建的arguments对象作为该函数活动对象的默认属性,创建时总会带有DontDelete标记。
  • 在eval代码块中声明的变量和方法都不带有DontDelete标记。
  • 对还不存在的变量或属性的直接赋值产生的对象不会带有任何标记,包括DontDelete。
  • 对于宿主对象而言,delete操作的结果有可能是不可预料的。

var关键字带来的不能被delete的问题也可以说是其副作用,需要根据具体的需求来选择应用场景。

上面所说的都是Javascript标准实现的内容,在非遵循标准实现的IE6-8下会有不同的问题,这就是IE常见的困扰我们的bug。

Javascript的对象作用域
1 vote, 4.00 avg. rating (80% score)

发表评论

电子邮件地址不会被公开。 必填项已用*标注


× 六 = 18

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">