变量作用域
一个变量的作用域是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中的任何地方都有定义的。然而在函数内声明的变量只在函数体内有定义。
在函数体内,局部变量的优先级高于全局变量。如果在函数体内声明一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量会被局部变量覆盖。var scope = "global scope" // 声明全局变量 function checkscope() { var scope = "local scope"; // 声明同名局部变量 function f() { return scope; // 返回局部变量的值 } return f() } checkscope()复制代码
函数定义是可以嵌套的。由于每个函数都有自己的作用域,因此会出现几个局部作用域的嵌套情况。
var scope = "global scope" // 全局变量 function checkscope() { var scope = "local scope"; // 局部变量 function nested() { var scope = "nested scoped" // 嵌套作用域内的局部变量 return scope; // 返回当前作用域内的值 } return nested() } checkscope()复制代码
作用域链
如果将一个局部变量看做是自定义实现的对象的属性的话,那么可以换个角度来解读变量作用域。每段JavaScript代码(全局代码或者函数)都有一个与之关联的作用域链。
作用域链的理解
这个作用域链是一个对象列表或链表,这组对象定义了这段代码“作用域中”的变量。当JavaScript需要查找变量x的值的时候(这个过程叫做变量解析),它会从链中的第一个对象开始查找,如果这个对象中有一个名为x的属性,则会直接使用这个属性的值;如果第一个对象没有这个名为x的属性,它会查找链上的下一个对象,以此类推。。。 如果作用域链上没有任何一个对象含有属性x,则认为改代码的作用链上不存在x,并最终抛出一个引用错误异常(ReferenceError)。
作用域链的组成
在JavaScript的最顶层代码中(不包含在任何函数定义内的代码),作用域链有一个全局对象组成。在不包含嵌套的函数体内,作用域上有两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局对象。在一个嵌套函数体内,作用域链上至少有三个对象。
理解对象链的创建规则: 当定义一个函数时,它实际上保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将找个对象添加至保存的那个作用域链上,同时创建一个新的更长的表示函数调用作用域的“链”。
扩展:
with语句
with语句用于临时扩展作用域链
它具有如下的语法:with(object)statement复制代码
这条语句将object添加到作用域链的头部,然后执行statement,最后把作用域链恢复到原始状态。
在对象嵌套层次很深的时候通常会使用with语句来简化代码编写。 例如,在客户端JavaScript中,可能会使用类似下面这种表达式来访问一个HTML表单中的元素:document.forms[0].address.value复制代码
如果这种表达式在代码中多次出现,则可以使用with语句将form对象添加至作用域链的顶层:
with(document.forms[0]){ // 直接访问表单元素,例如: name.value = ""; address.value = ""; email.value = "";}复制代码
这种方法减少了大量的输入,不用再为每个属性名添加document.forms[0]前缀。这个对象临时挂载在作用域链上,当JavaScript需要解析诸如address的标识符时,就会自动在这个对象中查找。当然,不使用with语句的等价代码可以写成这样
var f = document.forms[0];f.name.value = "";f.address.value = "";f.email.value = "";复制代码
不要忘记,只有在查找标识符的时候才会用到作用域链,创建新的变量的时候不使用它,看一下下面这行代码:
with(o)x = 1; 如果对象o有一个属性x,那么这行代码给这个属性赋值为1。但如果o中没有定义属性x,这段代码和不使用with语句的代码x=1是一模一样的。它给一个局部变量或者全局变量x赋值,或者创建全局对象的一个新属性。with语句提供了一种读取o的属性的快捷方式,但它并不能创建o的属性。
上一篇: 你不知道的javascript—作用域、闭包
查看原文