js作用域

  • 作用域

          作用域(scope)是结构化编程语言中的重要概念,它决定了变量的可见范围和生命周期,正确使用作用域可以使代码更清晰、易懂。作用域可以减少命名冲突,而且是垃圾回收的基本单元。和C、C++、         Java等常见语言不同,js的作用域不是以花括号包围的块级作用域(block scope),这个特性经常被大多数人忽视,因而导致莫名其妙的错误。例如下面代码,在大多数类C的语言中会出现变量未定义的错           误,而在js中却完全合法:

1 if(true){
2    var somevar = 'value';
3 }
4 console.log(somevar);//输出value

         这是因为js的作用域完全是由函数来决定的,if、for语句中的花括号不是单独的作用域。


  • 函数作用域

          不同于大多数类C的语言,由一对花括号封闭的代码块就是一个作用域,js的作用域是通过函数来定义的,在一个函数中定义的变量只对这个函数内部可见,我们称为函数作用域。在函数中引用一个变量         时,js会先搜索当前函数作用域,或者称为“局部作用域”,如果没有找到则搜索其上层作用域,一直到全局作用域,如以下例子:

 1 var v1 = 'v1';
 2 var f1 = function() {
 3    console.log(v1);//输出v1
 4 }
 5 f1();
 6 var f2 = function() {
 7 var v1 = 'local';
 8    console.log(v1);//输出local
 9 }
10 f2();

           console.log(v1);//输出v1,最外层可以看成全局函数(可能名称不对),f1和f2嵌套在该全局函数内
           以上示例十分明了,js的函数定义是可以嵌套的,每一层是一个作用域,变量搜索顺序是从内到外,下面这个例子可能就有些令人困惑:

1 var scope = 'global';
2 var f = function() {
3 console.log(scope);//输出undefined
4    var scope = 'f';
5 }
6 f();

           上面代码可能和你预想的不一样,没有输出global,二是undefined,这是为什么呢?这是js的一个特性,按照作用域搜索顺序,在console。log函数访问scope变量时,js会先搜索函数f的作用域,恰巧       在f作用域里面搜索到scope变量,所以上层作用域中定义的scope就被屏蔽了,但是执行到console。log语句时,scope还没被定义,或者说初始化,所以得到的就是undefined值了。
           我们还可以从另外一个脚本来理解:对于开发者来说,在访问未定义的变量或者定义了但没有初始化的变量时,获得的值都是undefined。于是我们可以认为,无论在函数内什么地方定义的变量,在一         进入函数时就被定义了,但直到var所在的那一行它才被初始化,所以在这之前引用到的都是undefined值。(事实上,js的内部实现并不是这样,未定义变量和值为undefined的变量还是有区别的。)

  •  函数作用域的嵌套

           接下来看一个稍微复杂的例子:

 1 var f = function() {
 2    var scope = 'f0';
 3    (function() {
 4        var scope = 'f1';
 5           (function() {
 6               console.log(scope);//输出f1
 7            })();  
 8        })();
 9    };
10 f();

            上面是一个函数作用域嵌套的例子,我们在最内层函数引用了scope变量,通过作用域搜索,找到了其父作用域中定义的scope变量。
      有一点需要注意,函数作用域的嵌套关系是定义时决定的,而不是调用时决定的,也就是说js的作用域是静态作用域,又叫词法作用域,这是因为作用域的嵌套关系可以再语法分析时确定,而不必等到运行         时确定。下面的例子说明了这一切:

 1 var scope = 'top';
 2 var f1 = function() {
 3    console.log(scope);
 4 };
 5 f1();//输出top
 6 var f2 = function(){
 7    var scope = 'f2';
 8    f1();
 9 };
10 f2();//输出top

            这个例子中,通过f2调用的f1在查找scope定义时,找到的是父作用域中定义的scope变量,而不是f2中定义的scope变量。这说明了作用域的嵌套关系不是在调用时确定的,而是在定义时确定的。

  •  全局作用域

           在js中有一种特殊的对象称为全局对象。这个对象在node.js对应的是global对象,在浏览器中对应的是window对应。由于全局对象的所有属性在任何地方都是可见的,所以这个对象又称为全局作用           域。全局作用域中的变量不论在什么函数中都可以被直接饮用,而不必通过全局对象。满足以下条件的属于全局作用域:
                 在最外层定义的变量;
                 全局对象的属性;
                 在任何地方隐式定义的变量(未定义直接赋值的变量)。
           需要格外注意的是第三点,在任何地方隐式定义的变量都会定义在全局作用域中,即不通过var声明直接赋值的变量。这一点经常被人遗忘,而模块化编程的一个重要原则就是避免使用全局变量,所以我       们在任何地方都不应该隐式定义变量。

原文地址:https://www.cnblogs.com/yuyuj/p/4525786.html