深入javascript之作用域

2018 年 9 月 29 日 0 条评论 1.84k 次阅读 0 人点赞

一,作用域
包括静态作用域和动态作用域,静态作用域也叫词法作用域,javascript采用的是静态作用域。

js中作用域是指可访问变量,对象,函数的集合,也就是调用它们能生效的代码区块。在js中没有块级作用域,只有全局作用域和函数作用域,可以模仿块级作用域,下面会讲到。

全局作用域
打开一个 js 文件,写了一行代码,这行代码所在的位置就会是全局作用域(global scope)。比如:

var name = 'hello world'

局部作用域
全局作用域只有一个,在全局使用域里面定义的其它的作用域都被称为局部作用域(local scope)。局部作用域是由函数创建的,每个函数都会创建一个局部作用域。

下面我创建了一个名字是 hello 的函数,在它里面声明了一个 name 变量,这个变量是在局部作用域之内。

// 作用域 A: 全局作用域

var hello = function() {
  // 作用域 B:局部作用域
  // 这里是由 hello 这个函数创建的局部作用域

  var name = '您好'
}

局部作用域
在这个局部作用域里面定义的东西,在这个作用域的外面是访问不到的。试一下:

var hello = function() {
  var name = '张三'
}

console.log(name)
// 返回:undefined

在 hello 函数里声明的 name 变量,在外面不能访问到,因为这个 name 是在 hello 函数创建的局部作用域之内。

词法作用域
词法作用域(Lexical Scope)。如果你在一个函数的内部又创建了一个函数,这个内部函数可以访问到外部函数的作用域。这就是词法作用域。

看个例子:

// 作用域 A 
var hello = function() {
  // 作用域 B:在这里定义了局部变量 name
  var name = "张三丰"
  var logger = function() {
    // 作用域 C:在这里使用了在 hello 那里声明的变量 name
    console.log(name)
  }
  logger()
}

hello()
// 输出 “张三丰”

hello 函数创建了一个作用域,在 hello 里面定义的 logger 函数也会创建一个作用域,logger 函数是在 hello 内部定义的,它可以访问到在它之外的作用域里的东西。这里就是我们在 logger 函数里输出了在 hello 创建的作用域里定义的 name 这个变量的值。

下面这个例子,name 变量在 function3,function2,function1 都是可以访问到的:

var name = '猫咪'
var function1 = function() {
  // 在这里可以访问到 name
  var function2 = function() {
    // 在这里也可以访问到 name
    var function3 = function() {
    // 在这里还可以访问到 name
    }
  }
}

假设你在 function3 里使用了 name 变量,JavaScript 在它里面没找到 name,就会往上一层继续寻找有没有 name,还没找到就会继续往上一层寻找,真到找到为止,如果最终结果还是没找到 name,就会报 undefined 。

 

1-1,全局,函数作用域
看个简单的例子

    var a = 10
    function f1(){
        var b = c = 20;
        console.log(a);     //10
        console.log(c);     //20
        function f2() {
           console.log(b); //20
        }f2();
    }
    f1();
    console.log(a);     //10
    console.log(c);     //20
    console.log(b);     //error

var b = c = 20  是指 var b = c; c = 20

在f1函数中c没使用var声明为全局变量,b为局部变量,绑定在f1函数下,外部访问不到。

1-2,模仿块级作用域
没有块级作用域,但是有if(),for()等块语句,在块语句内部定义的变量会保留在它们已经存在的作用域内,举个例子:

            if(true) {
                var word = 'hello';
                console.log(word);  //hello
            }
            console.log(word);      //hello

此时if()在add函数中,内部定义的变量存在于add函数的作用域中,只有在函数和块语句中才可以访问到,外部无法访问。

使用自执行的匿名函数包裹块语句构建块作用域,也叫私有作用域

function add(num) {
     for(var i = 0; i < num; i++) {
          console.log(i);	//0,1,2,3,4,5,6,7,8,9
     }
     console.log(i);	//10
 }
add(10);

将代码改为

function add(num) {
	 (function () {
		for(var i = 0; i < num; i++) {
	            console.log(i);  //0,1,2,3,4,5,6,7,8,9
	        }
	})()
        console.log(i);	//Uncaught ReferenceError: i is not defined
}
add(10);

此时变量i只能在for()循环中访问到,在add函数和外部都无法访问,并且在匿名函数中定义的任何变量都会在执行结束时被销毁,所以变量i只能在for()循环中使用。

在ES6中新增了块级作用域,let const声明的变量在块语句中声明时,外部无法访问。

二,结束语
为什么函数内部能访问到函数外部的变量?《深入javascript之执行上下文》已更新传送门

雷雷

这个人太懒什么东西都没留下

文章评论(0)

(Spamcheck Enabled)