本来这篇文章是紧跟着 this
篇就要出来的,不想竟时隔近半年之期。从执行上下文,到作用域甚至闭包,这是一个小体系,缺了作用域以及闭包这部分,使我学习《Javascript Ninja》中设计函数式编程的的地方尤为吃力。遂补全这二章。
作用域指定了变量或者函数的可用范围
了解作用域,就是了解作用域链。
初探作用域链
当 代码执行 时,会创建变量对象的一条作用域链。作用域链的前端,是当前执行上下文的变量对象(活动对象),作用域链的下一个变量对象来自外部的上下文,再下一个变量对象来自再下一个外部环境…作用域链的尾部则是全局上下文的变量对象。
我们先来看一个栗子:
|
|
bar
的作用域链是
|
|
作用域链其实就是将自身上下文,父上下文,一直延展到全局上下文的变量对象用一条链子连接起来的。
可能我们又要好奇了,作用域链是如何把它们串起来的呢?
详解作用域链
函数创建
当函数刚创建的时候,函数就天生自带了一个属性 [[scopes]]
,它是所有父变量对象的层级链。
|
|
当 foo
函数创建的时候,它就已经拥有 [[scopes]]
属性了。
|
|
函数调用
当 函数被调用 时,会创建当前函数的上下文:
|
|
此时才会创建作用域链。
我们在 Javascript深入浅出之this 那一章中讲到的 变量提升 之所以存在,是因为在所有代码执行之前, js 引擎会进行一个 标识符解析 的过程。
标示符解析是一个处理过程,用来确定一个变量(或函数声明)属于哪个变量对象。
我们继续来看一个栗子:
|
|
全局上下文的变量对象是:
|
|
当 foo
创建时, foo
的 [[scopes]]
属性为:
|
|
当 foo
被调用时, foo
上下文的 活动对象 和 作用域链 为:
|
|
当 bar
创建时, bar
的 [[scopes]]
属性为:
|
|
当 bar
被调用时, bar
上下文的 活动对象 和 作用域链 为:
|
|
标识符解析结果:
|
|
我们借助 Chrome Develop Tools 看一下:(代码有很大改动)
Some Cases
上面的例子展示的就是闭包的 [[scopes]]
特性,这里就不赘述了。
以构造函数创建的函数
|
|
这里 bar
函数之所以无法解析找到 a
,是因为以构造函数创建的函数的 [[scopes]]
属性指向的是全局变量。
这个还有一个延伸,就是严格模式下,是不允许使用 with
语句的,但是如果把语句放到 new Function 中,是被允许的。
|
|
new Function
产生的是 global
作用域下的函数,而且默认是 non-strict
。
延伸作用域链
有两种方法,with
和 try-catch
语句。
他们都遵循:Scope = withObject|catchObject + AO|VO + [[Scope]]
。
这里拿 with
举例并且延伸一下。
|
|
执行 with
语句时候,作用域最前端已经是 {a: 10}
了,我们在 with
语句里面改动的 a
是在 {a: 10}
中找到了,所以,全局作用域中的 a
并没有被修改。
作用域写完了,有一些边边角角的东西没写,不然展开的话篇幅太大。下篇文章就是闭包了,闭包和作用域真的是基友到爆,所以作用域一定要好好看,好好理解,多敲代码,跑到 Chrome 里去打几个断点测一测,效果会好很多。