JS作用域&作用域链

作用域&作用域链


作用域的概念

  • 变量作用域的概念:变量作用域就是一个变量可以使用的范围
  • JS中首先有一个最外层的作用域:称之为全局作用域。
  • JS中还可以通过函数创建出一个独立的作用域,其中函数可以嵌套,所以作用域也可以嵌套。
var age = 18; // age是在全局作用域中声明的变量,即为全局变量
function f1(){
    console.log(name); // 此处可以访问到name变量,输出undefined
    // 这里为何输出undefined,是由于变量提升造成的,见附录1
    
    // name是f1函数内部声明的变量,所以name变量的作用域就是在f1函数内部
    var name = "hello";
    console.log(name); // 可以访问到name变量
    console.log(age); // age是全局作用域中声明的变量,所以在f1函数内部也可以访问
}
console.log(age); // 此处可以访问
console.log(age); // 此处不可以访问name,因为name变量在函数f1内部声明的,属于局部变量,所以在全局作用域中无法访问。

上面就是一个简单的两级作用域栗子,下面我们写一个多级的作用域来引出作用域链的概念

作用域链

首先看一个多级作用域的栗子:

//多级作用域
//======>此处是1级作用域
var gender = "男";
function fn(){ // ======>从这里开始是2级作用域
    //gender 可以访问  gender是全局作用域的变量,任何地方都可以访问
    //age    可以访问
    //height 不能访问

    return function(){ // ======>从这里开始是3级作用域
        //gender  可以访问
        //age     可以访问
        //height  可以访问
        var height = 180;
    }
    var age = 5;
}
  • 由于作用域是相对于变量而言的,而如果存在多级作用域,这个变量又来自于哪里?这个问题就需要好好地探究一下了,我们把这个变量的查找过程称之为变量的作用域链

  • 作用域链的意义:查找变量(确定变量来自于哪里,变量是否可以访问)

  • 简单来说,查找一个变量来自哪里,能否被访问,需要以下四步:

    • 1.查看当前作用域,如果当前作用域声明了这个变量,可以直接访问
    • 2.查找当前作用域的上级作用域,也就是当前函数的上级函数,看看上级函数中有没有声明,有就返回变量,没有继续下一步
    • 3.再查找上级函数的上级函数,直到全局作用域为止,有则返回,无则继续
    • 4.如果全局作用域中也没有,我们就认为这个变量未声明(xxx is not defined)

这四步操作就描述了整个作用域链及作用域链如何查找变量的过程。

下面我们来举几个特殊的栗子:

  • 栗子1
function fn(callback){
    var age = 20;
    callback();
}
fn(function(){
    console.log(age);//报错
    //1.在当前作用域没有查找到age
    //2.查找上一级作用域:全局作用域
    //为何是全局作用域?因为看上一级作用域,不是看函数在哪调用,而是看函数在哪编写的。
    //这种特别的作用域,叫做“词法作用域”
})

这个栗子比较特殊,可能很多人会认为输出20,因为函数调用的地方是fn函数内部,恰巧age又是声明在这个函数内部的,理所应当输出20。这是错误的!
现在分析下作用域链如何查找变量的:

  1. console.log(age)的时候,在当前作用域并没有查询到age变量。所以查找上一级作用域。
  2. 上级作用域是谁?这里需要引出一个概念,查找函数上级作用域,不是看函数在哪调用,而是看函数在哪编写。所以这样来看,上级作用域就是全局作用域。
  3. 在全局作用域中并没有声明age变量,所以console.log(age);就会报错
  • 栗子2
    var name="张三";
    function f1(){
        var name = "abc";
        console.log(name);
    }
    f1(); // abc

如果查找一个变量时,在当前作用域找到变量,不管上级、上上级有没有同名变量都不会再去寻找。

  • 栗子3
    var name="张三";
    function f1(){
        console.log(name);
        var name = "abc";
    }
    f1(); // undefined

如果这个栗子能看懂说明已经了解变量提升,如果不懂请参考附录1

  • 栗子4
    var name = "张三";
    function f1(){
        var name = "abc";
        return function(){
            console.log(name);
            console.log(age);
        }
        var age = 18;
    }
    var fn = f1();
    fn();
    //abc 
    //undefined

这个栗子咋一看可能有点懵,但是记住之前一句很重要的话,查找函数上级作用域,不是看函数在哪调用,而是看函数在哪编写。

  • 栗子5
    var name="张三";
    function f1(){
        return {
            say:function(){
                console.log(name);
                var name="abc";
            }
        }
    }
    var fn=f1();
    fn.say();//undefined

当前作用域查到了变量,则不会再继续寻找,直接返回该变量的值,这里打印的时候变量声明但是未赋值,所以输出undefined。

  • 如果以上几个栗子都能看懂,说明你已经掌握了作用域&作用域链,学好作用域和作用域链可以为理解闭包打下很好的基础。

==附录1== 变量提升
变量提升的概念:函数和变量的声明会被js的解释器放到最上面。

    function fn1() {
      a = 1;
      console.log(a); 
      console.log(window.a); 
      var a = 5;
      console.log(a); 
    }
    fn1();

输出结果依次为:1 undefined 5,因为代码在解析的时候相当于

      var a;//函数的声明会被解释器放到头部,预先声明,但没有赋值,所以在此刻a是undefined
      a = 1;
      console.log(a); 
      console.log(window.a); //此处打印的是全局变量的a,但是并没有声明全局变量a
      a = 5;
      console.log(a); 
  • 函数的提升
    js中我们定义函数有两种方式:
    • 函数表达式 var fn=function fn(){}
    • 函数声明方式 function fn(){}

当函数是通过函数声明的方式定义时,就会有函数提升,
特别注意的是:

  • 变量提升,变量赋值并没有被提升,只是声明被提升了。
  • 但是,函数的声明有点不一样,函数体也会一同被提升。
test();

function test(){
    console.log(1);
}

所以会出现上面的情况,先执行test函数,然后声明,但是实际上函数确实被执行了,这就是与变量提升不同的点,函数提升不止声明提升,函数体也会一同被提升。
由于只是讲解作用域&作用域链的前置知识,简单介绍,不再赘述。想了解更多的关于变量提升的知识请查看我写的一篇专门讲解变量提升的文章。


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