JavaScript之作用域与作用域链

点击此处访问我的github了解更多详情

今天是2016的第一天,我们得扬帆起航踏上新的征程了。此篇阐述JavaScript中很重要的几个概念:作用域与作用域链及相关知识点。

我们先从变量与作用域的行为关系开始讨论。

变量作用域

JavaScript中,变量有全局变量及局部变量之分,而能定义变量作用域的语块只有函数。与局部变量有关的一种有趣特性,在此处不得不谈--变量提升。

变量提升

变量提升为何物?

JavaScript的变量声明会被提升到它们所在函数的顶部,而初始化仍旧在原来的地方。JavaScript引擎并没有重写代码:每次调用函数时,声明都会重新提升。


    var name = 'Jog'; //全局变量
    function prison() {
        console.log(a); //输出undefined
        var a = 1;//局部变量
        console.log(a); //输出1
        console.log(name); //输出Jog
    }
    prison();
    
    var name = 'Jog';
    function prison() {
        console.log(name); //输出undefined
        var name = 'Hans';
    }
    prison();

此处name的声明被提升到函数的顶部,变量查找时先从局部作用域开始,未找到则由内而外最后到全局作用域。

接下来我们详细分析一下JavaScript的提升方式。

变量提升与执行环境对象

学习任何一门语言,都像学习魔术一样,初时引人迷惑,惊叹;然而当秘密被揭开时几乎令人失望,JavaScript不外如是。

    1. 提升

执行某代码块时,JavaScript引擎先解释,再运行。解释过程主要几个过程:

- (1) 声明该作用域内var变量
- (2) 声明并初始化函数参数
- (3) 声明并初始化声明式函数

详细可查看本系列笔记JavaScript之解释与执行机制

    1. 执行环境与执行环境对象

执行环境(execution context)是一种概念,每当函数被调用都会产生一个新的执行环境。

执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为。

- 1. 每个函数都有自己的执行环境。  当执行流进入一个函数时,执行环境就被推入一个环境栈中;函数执行之后,栈将其执行环境弹出,控制权返回到之前的执行环境。
- 2. 如果变量在当前执行环境内可访问,则该变量在当前作用域内。
- 3. JavaScript访问变量,其实就是访问该执行环境对象(变量对象)中的属性。
- 4. 全局执行环境是最外围的一个执行环境。

执行环境对象--每个执行环境都有一个与之对应的变量对象,执行环境中定义的所有变量和函数都保存在这个对象中。


    function fn(arg) {
        var name = 'Far';
        inner();
        function inner() {
            console.log('inner');
        }
    }
    fn('test');

在调用fn时,其过程如下

- 1. 创建一个空执行环境对象;
- 2. 声明参数并赋值;{arg: 1}
- 3. 声明局部变量;{arg:1, name: undefined}
- 4. 预定义声明式函数;{arg:1, name: undefined, inner: function(){console.log('inner');}}
- 5. 代码执行时,局部变量被赋值;{...name: 'Far'...}
- 6. 执行环境对象上变量和函数属性保持不变,调用inner函数时,其内部会创建一个新的执行环境对象,依此可递归形成一条作用域链。

作用域与作用域链

当一个变量在某执行回家内可以被访问,我们称该变量在当前作用域内。

代码某一执行环境中执行时,会创建该执行环境对应的变量对象的一个作用域链。

JavaScript引擎在执行环境对象中查找作用域内的变量或函数,其查找顺序由内而外向上直到全局执行环境对象,这个顺序就形成作用域链

作用域链的前端,始终是当前执行环境对应的变量对象。若此执行环境是函数,则将其活动对象作为变量对象。作用域链中的下一个变量对象来自于当前变量对象的包含(外部)执行环境,如此一直到全局执行环境;全局执行环境的变量对象始终是作用域链中的最后一个变量对象。


    var age = 22;
    var country = 'China';
    var name = 'Java';
    var job = 'Web';
    function outer() {
        console.log(age);  //输出22
        console.log(country); //输出undefined
        var country = 'Union';
        var name = 'Python';
        inner();
        function inner() {
            console.log(name); //输出Python
            console.log(job); //输出Web
        }
    }
    outer();

代码输出结果如上:

- 1. outer函数执行时,首先在outer执行环境对象中查找age和country变量结果country存在但并未初始化赋值,输出undefined;而age未找到于是沿着作用域链向上到全局执行环境,在其变量对象中存在age属性,于是输出其值22.
- 2. inner函数执行时创建自己的执行环境对象,其并没有定义name和job等变量,于是沿着作用域链向上到达outer函数的执行环境,在其变量对象中存在name于是输出其值Python;而未找到job于是继续向上直到全局执行环境,找到并输出其值,结束;若依然未找到,则会报错,停止运行。

注:函数参数亦被当作变量对待,故其访问规则与普通变量相同。

延长作用域链

某些语句可以在作用域链的前端临时增加一个变量对象,该变量对象会在代码执行结束后移除。常见如:

  • try-catch语句;
    catch语句会创建一个新变量对象,包含被抛出的错误对象的声明。
  • with语句;
    with语句会创建一个包含语句接收对象的所有属性和方法的变量对象。

    function getAttr(data) {
        var obj = data;
        with(obj) {
            var o = location;
        }
        console.log(o);
    }
    getAttr(window);

上面with语句接收window对象,其创建的变量对象就包含了window对象所有属性和方法,于是可以在其执行环节直接访问location变量,也就是正常的window.location。

强烈建议不要使用with语句。

本篇笔记阐述JavaScript执行环境与执行环境对象,变量对象,作用域与作用域链,耗时四小时,还有诸多不足之处待日后补充改进。

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

推荐阅读更多精彩内容