• 函数作用域


    作用域的概念对于初学者来说可能比较难,它涉及到变量,函数等基础知识,理解作用域对于理解作用域链和闭包是非常重要的,今天闲来一起复习下作用域:

    1、定义

    作用域(scope)指的是变量可访问的范围,在JavaScript中只有两种作用域:一个全局作用域,另一个是函数作用域。全局作用域指变量在任何函数外声明(如果在函数内部声明的时候没有用var关键字,那就成了隐式全局声明了,也是全局变量,在严格模式下是禁止的),那么这样的话,在当前作用域下的任何地方都可以访问到这个全局变量;至于函数作用域,指的是变量在函数内部显式声明,这个变量只能在函数内部内被访问到,函数外部无法访问;

    在全局声明的变量成为全局变量,在函数内部定义的变量成为局部变量,局部变量只能在函数内部读取;

    var v = 1;
    
    function f(){
      console.log(v);
    }
    f()// 1

    在上面的代码中,变量v是全局中声明,为全局变量,在函数内就能读取到,所有输出结果为1

    然鹅,在函数内部定义的变量为局部变量,在函数外无法读取哦,如下:

    function f(){
      var v = 1;
    }
    //输出变量v console.log(v)
    // ReferenceError: v is not defined

    在上面的代码中,由于变量v是在函数f内部定义的,所以在函数外部无法读取到,因此会报undefined哦;

    如果在函数外部和内部同时出现同名变量呢?在函数内部读取的应该是全局的还是局部的呢?答案是局部变量,函数内部的局部变量会覆盖同名全局变量:

    var v = 1;//全局变量
    function f(){
      var v = 2;//局部变量
      console.log(v);
    }
    f() // 2
    console.log(v); // 1

    在上面的代码中,在全局和函数f中都有定义变量v,但是由于两个变量同名了,而且在f内部读取的变量v,所有局部变量v会覆盖掉全局变量v;

    这里有个点需要注意下,前面我一直说的是显式声明变量,如果在函数内部隐式声明了变量,即没有用var命令去声明变量,这个时候这个变量就是全局变量,在开发中,一定要避免隐式声明变量,解决办法是在采取严格模式,严格模式下禁止隐式声明变量,会报错;

    function fn(){
      x = 5;
    }; console.log(x);
    // 5

    在上面代码中,就是读取了一个函数内部的隐式全局变量,这个变量在哪里都可以访问到;

    2、变量提升

     JavaScript引擎的工作方式是,在代码正式执行之前,会有预解析的过程,先解析(通读)代码,获取所有被声明的变量和函数,然后再一行一行地运行。这造成的结果,就是所有的变量的声明语句和所有的函数在其作用域内,都会被提升到代码的头部,这就叫做变量提升(hoisting),看看栗子:

    console.log(a);//undefined
    var a = 1;

    上面的代码输出结果为undefined,为什么?因为在预解析阶段,变量a声明被提升到了顶部,到执行console.时,a只是声明了,并没有到赋值(js代码从上到下依次执行),此时不会报错,因为存在变量提升,真正是这样运行的:

    var a;
    console.log(a);//undefined
    a = 1;

    最后的结果是显示undefined,表示变量a已声明,但还未赋值。

    请注意,变量提升只对var命令声明的变量有效,如果一个变量不是用var命令声明的,就不会发生变量提升,因为没有用var声明,这个变量就是隐式全局变量,在全局都可以访问到,不会提升声明:

    console.log(b);//1
    b = 1;

    好,接下来,我们看看函数的声明提升,先看看函数有常见的几种声明方式:

    1. 使用function命令声明,如:function f1(){};
    2. 使用变量赋值方式声明,也叫函数表达式,如var f1 = funtion(){};
    3. 使用Function函数创建实例声明,如var f1 = new Function();(此方法不常用)

    看第一个,function命名声明函数,f1为函数名,这种方法声明的函数在预解析阶段也会发现声明提升,注意和变量不同的是,函数声明提升的时候,会同时赋值函数体:

    f1();
    function f1(){
      //some code here  
    };

    以上代码,虽然函数f1在声明之前就调用了,但正常执行,没毛病老铁,其实这么写就相当于下面这样:

    function f1(){
      //some code here  
    };
    f1();

    这是和变量声明不一样的地方;

    看第二种,第二种采用变量赋值的方式,这个时候就当函数体是一个值,而且函数在JavaScript中就是一个值,可以被任意赋值,那么就和普通变量声明提前一样了,在声明提升的时候不会发生赋值行为:

    f1();//Uncaught TypeError: f1 is not a function
    var f1 = function(){
    console.log("啊哈");
    };

    结果是f1报错undefined;

    至于第三种,和第二种一样,也是变量赋值形式,普通的变量提升;

    3、函数本身的作用域

    函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

    var a = 1;
    var x = function () {
      console.log(a);
    };
    function f() {
      var a = 2;
      x();
    }
    f(); // 1

    上面的代码中,由于x函数是定义在f函数外部的,所以x函数作用域绑定在外层,内部变量a不会到函数f体内取值,所以输出1,而不是2

    总之,函数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域。

    很容易犯错的一点是,如果函数A调用函数B,却没考虑到函数B不会引用函数A的内部变量。

    再来看个具体栗子:

    var x = function () {
      console.log(a);
    };
    
    function y(f) {
      var a = 2;
      f();
    }
    y(x);// ReferenceError: a is not defined

    上面代码将函数x作为参数,传入函数y。但是,函数x是在函数y体外声明的,作用域绑定外层,因此找不到函数y的内部变量a,导致报错。

    同样的,函数体内部声明的函数,作用域绑定函数体内部。

    function foo() {
      var x = 1;
      function bar() {
        console.log(x);
      }
      return bar;
    }
    var x = 2;
    var f = foo();
    f() // 1

    上面代码中,函数foo中定义了一个函数bar,在取出函数bar并调用的时候,bar内部的x变量访问的是外层函数foo中的变量x,而不是同名全局变量x,这也是前面讲的,内部同名变量会覆盖全局变量,这也是理解闭包的基础哦!

    今天到这里了,学习不停。。。。

  • 相关阅读:
    DB2 9 基础(730 检验)认证指南,第 5 局部: 措置惩罚 DB2 对象(4)
    DB2 9 底子(730 磨练)认证指南,第 7 部分: XQuery 简介(3)
    DB2 9 基础(730 检修)认证指南,第 5 局部: 措置 DB2 工具(2)
    DB2 9 根本(730 检修)认证指南,第 6 局部: 数据并发性(2)
    DB2 9 根基(730 测验)认证指南,第 7 部分: XQuery 简介(1)
    运用 KScope 阅读并编辑你的源代码
    DB2 9 根底(730 考试)认证指南,第 6 部分: 数据并发性(3)
    DB2 9 根底内情根基(730 测验)认证指南,第 5 部分: 措置 DB2 工具(5)
    DB2 9 底子根基(730 考验)认证指南,第 7 部门: XQuery 简介(4)
    DB2 9 根基内情(730 测验)认证指南,第 5 局部: 处理处罚 DB2 工具(6)
  • 原文地址:https://www.cnblogs.com/hjvsdr/p/7498213.html
Copyright © 2020-2023  润新知