一,作用域
包括静态作用域和动态作用域,静态作用域也叫词法作用域,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)