js中作用域的思考

和传统的C,C++等语言不同,javascript中的作用域不是以花括号包围的块级作用域(block scope),不妨看看以下代码:

if(true){
  var val = "test-val";  
}
console.log(val);

 在js中运行以上代码,控制台打印输出:“test-val”;而在C语言中,会报错,这是为什么呢?

  这是因为:JavaScript 的作用域完全是由函数来决定的


函数作用域

在一个函数中定义的变量只对这一个函数有效,我们称之为函数作用域。当在函数内使用一个变量时,js首先会搜索函数作用域,又称为局部作用域。如果未找到,则搜索上层作用域,直至全局作用域

举个栗子:

 

var name="global";
var fn1 = function (){
  console.log(name);  
}
var fn2 = function () {
  var name="area";
  console.log(name);
};

fn1(); //global
fn2(); // area

  就像你看到的,js的变量搜索是由内而外,函数作用域优先级最高!所以在执行fn2()时会优先取局部变量的值area。那么接下来的一短代码,可能会让你迷茫:

var name ="global";
var fn = function () {
  console.log(name);
  var name = "area";
}
fn();// undefined

  不要迷茫!这是因为在执行fn()时,在console.log函数访问到变量name时,会优先从函数作用域内进行搜索,而这时恰巧能搜索到变量name,则会屏蔽掉上级的变量name。又加上在执行console.log(name)时,name变量还未被定义,或者我们称之为位初始化,就会输出undefined。

也有一部分人把上述现象称之为:变量提升。所谓变量提升,指的是在刚进入函数时,函数内部的所有变量都被定义了,但直到遇见var的时候,该变量才被初始化。在初始化之前的所有的引用,都会得到undefined。

函数作用域的嵌套

 

(function () {
  var scope ="top";
  (function(){
    var scope = "middle";
    (function () {
      console.log(scope);//middle
    })()
  })()
})()

 

  为什么会输出middle呢?这是因为在访问最内层的变量scope时,会优先搜索函数作用域,结果没搜到,则由内而外搜到其父级作用域middle。

有一点需要注意:函数作用域的嵌套关系由定义时决定,而不是由引用时决定。举个栗子:

var name ="top";
var fn1 = function (){
  console.log(name);
}
fn1();//top var fn2 = function () { var name="middle"; fn1(); } fn2();//top

  为什么会得到top而不是middle呢?这是因为通过fn2调用fn1()时,访问的变量name不是fn2中的变量,而是其父级作用域的变量。 

全局作用域

在js中有一种特殊对象,在node中对应global对象,在浏览器中对应window对象。我们称之为全局对象,又称为全局作用域。它可以在js的整个作用域范围内被使用而不受限制。

满足以下几点可以称为全局对象:

» 在最外层定义的变量

» 全局对象的属性

» 变量的隐式声明,即不通过var声明的变量。

由于全局变量会造成变量污染,高度耦合性等缺点,所以应避免使用全局变量。

posted on 2018-02-28 15:59  李老头  阅读(612)  评论(0编辑  收藏  举报

导航