JavaScript拾漏之变量作用域

JavaScript拾漏之变量作用域

变量作用域

说到变量作用域,一般会有以下的说法:
在所有函数之外声明的变量,叫做全局变量,因为它可被当前文档中的其他代码所访问,其作用域称为全局作用域。在函数内部声明的变量,叫做局部变量,因为它只能在该函数内部访问,其作用域称为局部作用域(函数作用域、块作用域)。
但这样的说法是有问题的,不信试试下面的代码。

    var fo1 ="JavaScript";
    function foo1(){
       console.log(fo1);
       fo1 = "scope";
       console.log(fo1);
    }
    foo1();
    console.log(fo1);

    // JavaScript scope scope
    var fo2 ="JavaScript";
    function foo2(){
       console.log(fo2);
       var fo2 = "scope";
       console.log(fo2);
    }
    foo2();
    console.log(fo2);

    // undefined scope JavaScript

上面两段代码答案是什么?是不是和自己想的不一样?
下面对于变量的作用域知识做一些整理,也是对上面问题的一个解答。能力有限,有出入处,希望可以留言给予斧正。


变量的声明

JavaScript中三种声明

  • var
    声明变量,可选初始化值。
  • let
    声明块范围局部变量,可选初始化值。
  • const
    声明一个只读命名常量。
var和let的区别
  • var声明变量要么是全局的,要么是函数级的,而无法是块级的;let把变量的作用域限制在块级域中
    //var
    function varTest() {
      var fo1 = 31;
      if (true) {
        var fo1 = 71;  // same variable!
        console.log(fo1);  // 71
      }
      console.log(fo1);  // 71
    }

    //let
    function letTest() {
      let fo2 = 31;
      if (true) {
        let fo2 = 71;  // different variable
        console.log(fo2);  // 71
      }
      console.log(fo2);  // 31
    }
  • let的作用域是块,而var的作用域是函数
    var fo1 = 5;
    var fo2 = 10;

    if (fo1 === 5) {
      let fo1 = 4; // The scope is inside the if-block
      var fo2 = 1; // The scope is inside the function

      console.log(fo1);  // 4
      console.log(fo2);  // 1
    } 

    console.log(fo1); // 5
    console.log(fo2); // 1

可以用let 来代替var ,在for定义块中使用块级变量.

    for (let i = 0; i < 10; i++) {
      console.log(i); // 0, 1, 2, 3, 4 ... 9
    }

    console.log(i); // i is not defined
声明全局作用域
  • 显示声明
    在函数体(function)外声明
    var fo;
    fo = "global variable"

    function foo() {
        alert(fo);
    }
  • 隐式声明
    隐式声明时,变量默认声明为全局作用域
    // 函数外隐式声明
    fo2 = "global variable"
    function foo2() {
        console.log(fo2);   // "global variable"
    }

    // 函数体内隐式声明
    function foo3(){
        fo3 = "global variable"
    }
    // 函数内的声明要有效,则需要先调用函数,否则会出现ReferenceError: fo3 is not defined
    console.log(fo3);   // ReferenceError

    // 调用函数
    foo3(); 
    // 在函数调用后,隐式声明的fo3变量默认声明为全局作用域
    console.log(fo3);   //"global variable"
  • window声明全局变量
    每个 JavaScript 环境有一个全局对象,当你在任意的函数外面使用 this 的时候可以访问到。你创建的每一个全部变量都成了这个全局对象的属 性。在浏览器中,方便起见,该全局对象有个附加属性叫做 window,此 window(通常)指向该全局对象本身。
    myglobal = "hello"; // 不推荐写法
    console.log(myglobal); // "hello"
    console.log(window.myglobal); // "hello"
    console.log(window["myglobal"]); // "hello"
    console.log(this.myglobal); // "hello"

这种方式经常被用到一个匿名函数执行后将一些函数公开到全局。 如JQuery中:

    window.jQuery = window.$ = jQuery;

声明局部作用域

JavaScript 中局部变量只可能通过两种方式声明,一个是作为函数参数,另一个是通过 var 关键字声明。

    // 全局变量
    var fo = 1;
    var bar = 2;
    var i = 2;

    function foo(i) {
        // 函数 foo 内的局部作用域
        i = 5;

        var fo = 3;
        bar = i + 4;
    }
    foo(10);

fo 和 i 是函数 foo 内的局部变量,而此时的bar是隐式声明,赋值将会覆盖全局作用域内的同名变量。
此时的i为局部变量,而局部变量在函数内优先级高于全局变量,所以此时函数内的i为5。而bar是隐式声明,经过foo(10)函数的调用后,会导致函数内的bar转为全局变量,会覆盖函数外声明的全局变量值。而fo由于函数内是显示声明,所以函数内的fo不会对函数外的fo影响。

声明全局和局部作用域的区别

声明变量(Declared Variable)和非声明变量(Undeclared Variable)的区别是:
- 声明变量的作用域限制在其声明位置的上下文中,而非声明变量总是全局的

    function foo() {
      fo1 = 1;   // 在严格模式(strict mode)下会抛出ReferenceError异常。
      var fo2 = 2;
    }

    foo();

    console.log(fo1); // "1" 。
    console.log(fo2); // ReferenceError: fo2 is not defined; fo2未在foo内部声明
  • 声明变量在任何代码执行前创建,而非声明变量只有在执行赋值操作的时候才会被创建
    console.log(fo); // ReferenceError: fo is not defined。
    console.log('still going...'); // 永不执行。
    var fo;
    console.log(fo); // "undefined"或""(不同浏览器实现不同)。
    console.log('still going...'); // "still going..."
  • 声明变量是它所在上下文环境的不可配置属性(non-configurable property),非声明变量是可配置的(例如非声明变量可以被删除)。即,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性
    var fo1 = 1;
    fo2 = 2;

    // 在严格模式(strict mode)下抛出TypeError,其他情况下执行失败并无任何提示。

    delete this.fo1;    // 非严格模式下,false
    delete this.fo2;    // true 

    console.log(fo1, fo2); // 抛出ReferenceError。
    // 'fo2'属性已经被删除

var创建的全局变量(任何函数之外的程序中创建)是不能被删除的
无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的


    // 定义三个全局变量
    var global_var = 1;
    global_novar = 2; // 反面教材
    (function () {
       global_fromfunc = 3; // 反面教材
    }());
    // 试图删除
    delete global_var; // false
    delete global_novar; // true
    delete global_fromfunc; // true
    // 测试该删除
    typeof global_var; // "number"
    typeof global_novar; // "undefined"
    typeof global_fromfunc; // "undefined"

由于以上三个区别,声明变量的错误会导致不可预测的结果。于是,无论是否在函数中,我们总是推荐使用声明变量。在ECMAScript 5的严格模式(strict mode)下,给一个未声明的变量赋值会抛出错误。


总结

  • JavaScript没有块作用域
  • 最外层函数和在最外层函数外面定义的变量拥有全局作用域
  • 给一个非声明变量赋值会隐式创建一个全局变量(全局object的一个属性),即是说所有末定义直接赋值(隐式声明)的变量自动声明为拥有全局作用域
  • 所有window对象的属性拥有全局作用域
  • JavaScript 通过函数管理作用域。在函数内部声明的变量只在这个函数内部,函数外面不可用。另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。

参考内容

深入理解JS中的变量作用域
Javascript:谈谈JS的全局变量跟局部变量
JavaScript声明全局变量三种方式的异同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值