8.8. 函数作用域与闭包 如第四章所述,JavaScript函数的函数体在局部作用域中执行,局部作用域不同于全局作用域.本章将解释这些内容和相关的作用域问题,包括闭包.[*] [*] 本章包含超前的内容,如果你是第一次阅读,可以跳过. 8.8.1. 词法作用域(Lexical Scoping) JavaScript中的函数是基于词法作用域的,而不是动态作用域.这句话的意思是JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里.定义一个函数时,当前作用域链被保存起来并成为该函数内部状态的一部分.作用域链的顶层(最初一层)是由全局对象构成的,这和词法作用域没什么明显的关联.然而,当你定义一个嵌套函数时,作用域链将包含外层函数(嵌套函数的外层函数.原文:the containing function).这就意味着,被嵌套的函数可以访问外层函数的所有参数和局部变量. 注意:尽管在一个函数定义的时候,作用域链就已经固定了,但是作用域链中定义的属性并不是固定的.作用域链是”活的”(“live”),当函数被调用的时候,它有权访问任何当前被关联的数据. 8.8.2. 调用对象(The Call Object) 当JavaScript解释器调用函数的时候,首先,它把作用域设置到作用域链,在函数被定义的时候,该作用域链已经有效.接下来,解释器添加一个叫做调用对象(ECMAScript规范使用术语:activation object,活动对象)的对象到作用域链的头部.引用Arguments对象的arguments属性为函数初始化调用对象.接下来,添加函数的命名参数到调用对象.所有用var语句定义的局部变量也都在这个对象中定义.因为调用对象在作用域链的头部,局部变量,函数参数和参数对象都在函数的作用域内.也就是说它们隐藏了所有同名的在更早的作用域中定义的属性. 注意:与arguments不同,this是关键字,而不是调用对象的一个属性. 8.8.3. 调用对象作为命名空间(The Call Object as a Namespace) 有时,用定义一个简单函数的方法创建一个调用对象是很有用的,这个调用对象可以扮演一个临时命名空间的角色,如此一来,你定义的变量和创建的属性都不会破坏全局命名空间.例如:假设你有一个Javascrip代码文件,你希望把它用到很多不同的Javascript程序中(或者,用于客户端Javascript,在很多不同的web前端网页上).假设这些代码像其它代码一样定义了中间变量来保存计算结果.现在的问题是因为这些代码将用于很多不同的程序,你无法知道此变量是否和其它引入该文件的程序的变量相冲突. 解决的方法是把代码放到函数里,然后调用这个函数.如此一来,变量是被定义在函数的调用对象中: function init() { // 代码从这里开始 // 任何变量声明都会成为调用对象的属性 // 如此不会破坏全局命名空间. } init(); // 不要忘了调用这个函数哦! 这段代码只给全局命名空间添加了一个”init”属性,该属性引用init函数.如果定义一个函数还嫌太多,那么你可以用一个表达式定义和调用一个匿名函数.像这样的JavaScript语法如下: ( function () [...]
Archive of posts tagged 作用域