JavaScript的作用域和变量对象

变量对象

先来说说什么是变量对象,变量对象中又存储了什么东西吧。 JavaScript中的执行环境包括全局执行环境和函数执行环境这两种,每进入到一个执行环境都会创建一个变量对象,这个对象中记录了在当前执行环境中可以访问到的变量,它们以变量对象的属性形式存在。也就是说这个变量对象成为“作用域”这个抽象概念的实体。 同时,变量对象中的属性记录是有一定先后顺序的,并且属性值到底是实际的值还是undefined也是分阶段的(进入上下文(函数开始调用,但还未执行内部的具体代码);执行代码阶段)。

1. 在全局环境中:

(1)函数声明 (2)变量声明—其值为undefined,一直到执行到这条语句的时候才被赋予具体值。

2. 函数环境中:

在进入执行环境的时候,变量对象会进行如下初始化: (1)arguments对象,对象中的值被赋予具体的实参值 (2)函数的形参:变量对象的一个属性,其属性名就是形参的名字,其 值就是实参的值;对于没有传递的参数,其值为undefined。 (3)函数声明:变量对象的一个属性,其属性名和值都是函数对象创建出来的,其值为指向某个函数对象的引用; (4)变量声明:变量对象的一个属性,其属性名即为变量名,其值为undefined。 而在执行到函数内部的具体某个语句的时候,上面所述的值为undefined的变量,其值都会被赋予具体的值。

总结一句话:在进入上下文的时候(比如进入到一个全局环境或者是调用某个函数),变量对象除了arguments,函数的声明,以及参数被赋予了具体的属性值,其它的变量属性默认的都是undefined。

关于声明提升

**

JavaScript具有“声明提升”的特性。使用function或var进行的声明会被提升至当前作用域的顶端。

** 其实理解这句话可以从进入到一个执行环境后都发生了什么来看,正如上面所说,进入到一个上下文后,声明的函数(值为函数对象的引用)和变量(其值为undefined)都被记录在了变量对象中。到了执行代码阶段,一边在作用域链中搜索标识符,一边执行到某个语句后把原本进入上下文阶段值为undefined的变量更换为具体的值。

函数声明提升 看看第(3)条,在进入到执行环境的时候,函数声明就会被添加到变量对象中,其对应的属性值是指向所声明的函数对象的引用,如果在函数声明的语句之前存在一条函数调用语句,,引擎会在作用域中搜索该标示符,并且在变量对象中成功查找到了声明的函数名,其值也是存在的,即指向创建的函数对象的引用。因此这样写代码不会报错,成功调用了函数。

而通过函数表达式方式,也就是说不是以function开头,而是把函数赋值给了某个变量,因为在进入上下文阶段是不会给变量赋予具体的值的(参看第(4)条),所以这个时候该变量的值为undefined,要等到执行代码阶段,执行到这条语句后才会将其值更换为指向函数对象的引用,因此如果调用函数的语句在前面则会报错,此时在变量对象中搜索标识符,其值还是undefined,当然不能成功调用了。

再来看一个变量声明提升的例子

var a = “Hello”;{alert(a); //undefinedvar a = “World”;alert(a); //World}b();

为什么执行了b(),第一个alert(a)是undefined呢?来看看进入到全局执行环境中发生了什么,全局环境的变量对象被填充为: VO={ b:指向一个函数对象的引用, a:undefined—>执行到该句话后变成”hello” } 再来看看调用b()发生了什么,其变量对象被填充为: VO={ arguments:{}, a:undefined—->执行到具体语句后才被赋值为“world” } 注意到第一次执行到 alert(a)时会在当前作用域中搜索标识符a,发现已经存在一个a(则不会再沿着作用域链往外层搜索了),其值是undefined。而执行到var a = “World”;时则被替换为了具体的值。 如果在b中去掉var,则会沿着作用域链搜索到全局环境中的变量a,那么两个alert(a)都会输出“hello”。

参考资料:javascript 执行环境,变量对象,作用域链 JavaScript中的作用域与变量声明提升

一个人的天地是冷得连呼吸都会寂寞的颤栗,而麻烦,

JavaScript的作用域和变量对象

相关文章:

你感兴趣的文章:

标签云: