javascript作用域链详解
文章部分实例和内容来自鸟哥的blogJavascript作用域原理
首先应该注意几个点:
函数也是对象
variable object(VO)
A variable object is a container of data associated with the execution context. It’s a special object that stores variables and function declarations defined in the context.
var foo=10; function func(){}; //因为是在全局作用域当中,so... Global VO={ foo:10, func:<function> }
Activation Object(AO)
When a function is activated (called) by the caller, a special object, called an activation object is created.
It’s filled with formal parameters and the special arguments object (which is a map of formal parameters but with index-properties). The activation object then is used as a variable object of the function context.
A function’s variable object is the same simple variable object, but besides variables and function declarations, it also stores formal parameters and arguments object and called the activation object.
function foo(x,y){ var z=30; function bar(){}; } foo(10,20); //当执行到foo(10,20)时即会产生AO Activation Object={ z:30, x:10, y:20, bar:<function>, arguments:{0:10,1:20,length:2} }
Scope Chain(Scope Chain=Activation Object + [[scope]])
A scope chain is a list of objects that are searched for identifiers appear in the code of the context.
在JavaScript当中,函数的运行是在它被定义的作用域当中,而非执行的作用域当中。
先看一段代码:
var name="laurence?"; function show(){ console.log(name); var name="laurence?"; console.log(name); }最后输出: undefined laurence?Object={ name1:undefined,//第一个输出为undefined,表达式声明的局部变量覆盖全局变量,函数执行到这一语句的时候才进行赋值操作name="laurence?",之前name=undefined name2:"laurence?",//第二个输出为"laurence?"}window={ name:"laurence?", show:function()}
注意点:
函数也是对象
变量提升
函数在定义过程中,会将定义时刻的
scope chain
链接到这个函数对象的[[scope]]
属性上,这个属性包含了函数被创建的作用域中 对象 的集合,同时它的作用域会被创建此函数的作用域中可访问的数据对象填充。(**对象的集合、对象链**)函数的执行过程中,会创建一个 活动对象 (
activation object
),该对象包含了所有的局部变量
、命名参数
、参数集合
和this
,然后将这个活动对象作为此时作用域链的最前端,每个活动对象都有自己的作用域链,用于标识符的解析,当活动对象被创建时,而它的作用域初始化为当前运行函数的[[scope]]
所包含的对象。
var func=function(lps,rps){ var name="XL"; ....}func();
var func=function(){ }
相当于匿名函数的执行(在执行函数创建活动对象
(Obj)
的过程中,会创建一个arguments
属性,然后会给这个活动对象添加2个属性名,Obj.lps
,Obj.rps
对于每一个在这个函数中申明的局部变量和函数定义,都作为该活动对象的同名属性,然后将调用参数赋值给形参,对于缺少的调用参数,赋值为undefined
)
//这里func()执行时Obj(AO)={ lps:undefined, rps:undefined, arguments:{} name:"XL"}//创建func()时为全局对象:window(Global VO)={ func:function()}
实际的例子:
function factory(){ var name="laruence"; var intro=function(){ console.log("I'm "+name); } return intro;}function app(para){ var name=para; var func=factory(); func();}app("eve");
当调用app的时候,scope chain是由{window活动对象(全局)}
+{app活动对象}
组成
此时的[[scope chain]]
为(**可访问的数据对象访问**):
[[scope chain]]=[Active Object={ this:window, arguments:{0:"eve",length:1} name:'eve' func:<function> para:"eve",},Global VO={ this:window, app:<function>, window:<object>, document:<object>}]
当调用进入factory
函数体内时(**注意这里,函数的scope chain是在它被定义的时候决定的,而非执行的时候决定的**),此时的factory
的scope chain为:
[[scope chain]]={Active Object={ this:window, arguments:{}, name:"laruence", intro:<function>,},Global Object(Variable Object)={ this:window, factory:<function>, app:<function>, window:<object>, document:<object>}}
在定义intro
函数的时候,intro
函数[[scope]]为:
[[scope chain]]={Object={ name:"laruence", intro:<function>, this:<factory>, //注意这里的this指向 arguments:{}},Gloabal Object={ this:window, factory:<function>, document:<object>, window:<object>}}
从factory
函数返回后,在app体内调用intro时,发生了标识符的解析,而此时的scope chain是:
[[scope chain]]={ intro AO={ <intro 活动对象> } , Factory AO={ name:"laruence", intro:<function>, }, Global VO={ this:window, factory:<function>, document:<obeject>, window:<object> }}
在intro执行过程中scope chain
不包含app活动对象,因此name标识符解析的结果应该是factory活动对象中的name属性,也就是"laruence"
预编译
JS在执行每段代码前都会首先处理var
关键字(**函数定义式**)和function
定义式(**函数声明式**)
变量提升(hoisting
)
1.javascript-the-core(强烈推荐)
2.理解javascript作用域和作用域链