JavaScript—作用域学习
执行环境: 在《JavaScript高级程序设计》第三版如下定义: 执行环境定义了变量或函数有权访问的其他数据, 决定它们各自的行为。 每个执行环境都有一个与之关联的变量对象, 环境中定义的所有变量和函数都保存在这个对象中。
当代码在一个环境执行时, 会创建变量对象的一个作用域链。 作用域链的用途: 保证对执行环境有权访问的所有变量和函数的有序访问。 作用域链的最前端, 始终都是当前执行的代码所在环境的变量。 如果这个环境是函数, 则将其活动对象作为变量对象。
作用域链中的下一个变量对象来自包含(外部)环境, 而再下一个变量对象则来自下一个包含环境。 这样子, 一直延续到全局执行环境。 即: 全局执行环境的变量对象始终都是作用域链中的最后一个对象。
标识符的解析: 沿着作用域链一级一级地搜索标识符的过程。始终从作用域链的最前端开始, 然后逐级向后回溯, 直到找到标识符。(如果找不到, 则被定义为未定义, 通常会导致错误)
以下为《高性能JavaScript》中的理解:
每个JavaScript函数都表示一个对象, 确切的为一个Function的实例对象。Function对象跟其他对象一样,拥有可以编程访问的对象,和不可以通过代码访问的仅供JavaScript引擎存取的内部属性。其一为[[Scope]], 由ECMA-262标准第三版定义。
作用域链: 内部属性[[Scope]]包含了一个函数被创建的作用域中对象的集合。 它决定哪些数据能被函数访问。
函数作用域中的每个对象被称为一个可变对象,每个可变对象都以‘键值对‘的形式存在。
当一个函数创建后, 他的作用域链会被创建此函数的作用域中可访问的数据对象所填充。
执行函数时会创建一个称为执行环境(execution context)的内部对象。 函数每次执行时对应的执行环境都是独一无二的, 所以多次执行同一个函数, 也会创建多个执行环境。 当函数执行完毕, 执行环境就会注销。
每个执行环境都会有自己的作用域链, 用于解析标识符。 当执行环境被创建时, 它的作用域链初始化为当前运行函数的[[Scope]]属性中的对象。 这些值按照他们出现在函数中的顺序被复制到执行环境的作用域链中。
这一过程一旦完成, 一个被称为“活动对象(activation Object)”的新对象就被执行环境所创建。
活动对象(activation Object): 作为函数运行时的变量对象, 包含了所有的局部变量,命名参数, 参数集合以及this。 然后, 此活动对象被推入作用域链的最前端。 当执行环境被销毁, 活动对象也同时被注销。
关于作用域链的性能问题: 在函数执行的过程中, 每遇到一个变量, 都会经历一次标识符解析过程以决定从哪里获取或存储数据, 该过程搜索执行环境的作用域链, 查找同名的标识符, 搜索过程从作用域的最前端开始
也就是当前运行函数的活动对象, 找到就使用该对象, 找不到会继续搜索作用域链中的下一个对象, 知道最末端为止。 如果找不到, 那么标识符将被定义为: 未定义。
延长作用域链: 1)try-catch语句
2)with语句
两个语句都会在作用域链的最前端添加一个变量对象。 对with语句来说, 会将指定的对象添加到作用域链中。 对于catch语句来说, 会创建一个新的变量对象, 其中包含的是被抛出错误对象的声明。
——《JavaScript高级程序设计》
with语句: 当代码执行时, 执行环境的作用域链临时被改变了, 一个新的变量对象被创建, 它包含了参数指定的对象的所有属性, 这个对象会被推入到作用域链的首位, 以为这函数的所有的局部变量将处于第二个作用域链对象中, 因此访问代价更高了。
try-catch语句: try中发生错误, 执行过程会自动跳转到catch语句, 然后把异常对象推入一个变量并置于作用域首位。 在catch代码内部, 函数的所有局部变量将处于第二个作用域链对象中, 因此访问代价更高了。 性能优化为: 将错误委托给一个函数处理。

浙公网安备 33010602011771号