由一道题图解JavaScript的作用域

作用域

为了理解作用域,跪看了好几篇大神的博文,终于略知一二。

1.题目

其中,看到这样一道题(稍作修改):

 function factory() {
     var name = 'laruence';
     var intro = function(){
          console.log('I am ' + name);
          console.log('I am ' + age + "years");
     }
     return intro;
}
 
function app(para){
     var name = para;
     var age = 20;
     var func = factory();
     func();
}
 
app('eve');

运行结果是:

I am laruence
脚本报错:Uncaught ReferenceError: age is not defined

虽然在不理解作用域的情况下,我也做出了正确答案。但当我看了网上大神对作用域的讲解后,再来看此题,越看越经典,当初能做对,纯粹运气。当我将此题的作用域链画出来之后,终于感觉作用域入门了。

2.图解作用域

(1)JS引擎在进入一段可执行的代码时,需要完成以下三个初始化工作:

  • 创建一个全局对象(Global Object):全局对象在创建时,将Math,String,Date,document 等常用的JS对象作为其属性。还有另外一个属性window,将window指向了自身,这样就可以通过window访问这个全局对象了。
  • 构建一个执行环境栈( Execution Context Stack):当执行一个函数时,该函数的执行环境就会被推入执行环境栈的顶部并获取执行权。当这个函数执行完毕,它的执行环境又从这个栈的顶部被删除,并把执行权并还给之前执行环境。
  • 创建一个全局执行环境(Execution Context)EC,并将这个全局执行环境EC压入执行环境栈中。
  • 创建一个与EC关联的全局变量对象(Varibale Object) VO,并把VO指向全局对象。VO中不仅包含了全局对象的原有属性,还包括在全局定义的变量和函数。同时,在定义函数A的时候,还为 A 添加了一个内部属性scope,并将scope指向了VO。每个函数在定义的时候,都会创建一个与之关联的scope属性,scope总是指向定义函数时所在的环境。
    图片描述
    图片描述

(2)当执行进入app('eve') 时

  • 创建函数app的执行环境EC(a):然后EC(a)推入执行环境栈的顶部并获取执行权。
  • 创建函数app的作用域链(Scope Chain):在javascript中,每个执行环境都有自己的作用域链,用于标识符解析,当执行环境被创建时,它的作用域链就初始化为当前运行函数的scope所包含的对象。
  • 创建一个当前函数的活动对象(Activation Object) AO:AO中包含了函数的形参、arguments对象、this对象、以及局部变量和内部函数的定义,然后AO会被推入作用域链的顶端。需要注意的是,在定义函数B的时候,JS引擎同样也会为B添加了一个scope属性,并将scope指向了定义函数B时所在的环境,定义函数B的环境就是A的活动对象AO, 而AO位于链表的前端,由于链表具有首尾相连的特点,因此函数B的scope指向了A的整个作用域链。


    图片描述
    图片描述

(3)当执行进入factory() 时

  • 创建函数factory的执行环境EC(f):然后EC(f)推入执行环境栈的顶部并获取执行权。
  • 创建函数factory的作用域链Scope Chain(f):在javascript中,每个执行环境都有自己的作用域链,用于标识符解析,当执行环境被创建时,它的作用域链就初始化为当前运行函数的scope所包含的对象。
  • 创建一个当前函数的活动对象AO(f):AO中包含了函数的形参、arguments对象、this对象、以及局部变量和内部函数的定义,然后AO(f)会被推入作用域链的顶端。需要注意的是,在定义函数intro的时候,JS引擎同样也会为intro添加了一个scope属性,并将scope指向了定义函数intro时所在的环境,定义函数intro的环境就是factory的活动对象AO(f), 而AO(f)位于链表的前端,由于链表具有首尾相连的特点,因此函数intro的scope指向了factory的整个作用域链。


    图片描述
    图片描述

(4)当执行进入func()时
函数factory被执行以后,返回了intro的引用,并赋值给了变量func,执行 func() 就相当于执行intro()。

  • 创建函数intro的执行环境EC(i),然后EC(i)推入执行环境栈的顶部并获取执行权。 此时执行环境栈中有三个执行环境,分别是全局执行环境、函数app的执行环境和函数intro,intro的执行环境在栈顶,全局执行环境在栈的底部。
  • 创建函数intro的作用域链Scope Chain(i),并初始化为函数intro的scope所包含的对象,即包含了factory的作用域链。
  • 创建函数intro的活动对象AO(i),并将intro的arguments对象和this对象作为AO(i)的属性。


    图片描述
    图片描述

执行func()时,它的作用域链为 VO(G ) <- AO(f) <- AO(i),所以

  • 读取name属性时,AO(i)对象上没有name属性,沿着作用域链到AO(f),读取到了name属性,值为"laruence";
  • 读取age属性时,其实你会发现只有AO(f)上定义了age属性,而AO(f)又不在作用域链上,所以此时会报脚本错误Uncaught ReferenceError: age is not defined,表示age属性未定义。这也从侧面证明了我们上面的作用域链分析是正确的。

执行完成,退出。

3.Javascript中的作用域

  • JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。
  • 在JS中,每次调用一个函数的时候 ,就会进入一个函数内的作用域,当从函数返回以后,就返回调用前的作用域。
  • 在javascript中,每个函数都有自己的执行环境,当执行一个函数时,该函数的执行环境就会被推入执行环境栈的顶部并获取执行权。当这个函数执行完毕,它的执行环境又从这个栈的顶部被删除,并把执行权并还给之前执行环境。
  • 每个函数在定义的时候,都会创建一个与之关联的scope属性,scope总是指向定义函数时所在的环境。
  • 在javascript中,每个执行环境都有自己的作用域链,用于标识符解析,当执行环境被创建时,它的作用域链就初始化为当前运行函数的scope所包含的对象。
  • 有了作用域链, 在发生标识符解析的时候, 就会逆向查询当前scope chain列表的每一个活动对象的属性,如果找到同名的就返回。找不到,那就是这个标识符没有被定义。
  • 全局对象(Global Object) , 这个对象全局只存在一份,它的属性在任何地方都可以访问,它的存在伴随着应用程序的整个生命周期。全局对象在创建时,Math,String,Date,document 等常用的JS对象作为其属性。
  • 活动对象(Activation Object) AO,这里的活动对象扮演着变量对象的角色,只是在函数中的叫法不同而已(你可以认为变量对象是一个总的概念,而活动对象是它的一个分支), AO中包含了函数的形参、arguments对象、this对象、以及局部变量和内部函数的定义,然后AO会被推入作用域链的顶端。

参考文献

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容