作用域
概念
- 作用域是在运行时代码中的某些特定部分中变量,函数和对象的可访问性。换句话说,作用域决定了代码区块中变量和其他资源的可见性。
- 作用域是根据名称查找变量的一套规则。作用域是一套规则,用于确定在何处以及如何查找变量(标识符)—— 《你不知道的JavaScript》
例如:某个变量在全局都可以被访问,那就可以说这个变量拥有全局作用域。
作用域可以分为全局作用域,局部作用域(函数作用域)和块级作用域。ES6的到来,为我们提供了‘块级作用域’,可通过新增命令let和const来体现。
全局作用域
在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
- 最外层函数 和在最外层函数外面定义的变量拥有全局作用域
var name = "hello World";
// 此处可调用 name 变量
function myFunction() {
// 函数内可调用 name 变量
}
- 所有末定义直接赋值的变量自动声明为拥有全局作用域
// 此处可调用 name 变量
function myFunction() {
name = "hello World";
// 此处可调用 name 变量
}
- 所有window对象的属性拥有全局作用域
全局作用域有个弊端:如果我们写了很多行 JS 代码,变量定义都没有用函数包括,那么它们就全部都在全局作用域中。这样就会 污染全局命名空间, 容易引起命名冲突。
局部作用域
变量在函数内声明,变量为局部作用域,只能在函数内部访问。
// 此处不能调用 name 变量
function myFunction() {
var name = "hello World";
// 函数内可调用 name 变量
}
局部变量只作用于函数内,所以不同的函数可以使用相同名称的变量。局部变量在函数执行时创建,函数执行完毕后局部变量就会自动销毁。
JavaScript变量生命周期,局部变量函数执行完毕后销毁,全局变量在页面关闭后销毁。函数参数只在函数内起作用,属于局部变量。
作用域是分层的,内层作用域可以访问外层作用域的变量,反之则不行。
块级作用域
块级作用域可通过新增命令let和const声明,所声明的变量在指定块的作用域外无法被访问。块级作用域在如下情况被创建:
- 在一个函数内部
- 在一个代码块(由一对花括号包裹)内部
let 声明的语法与 var 的语法一致。你基本上可以用 let 来代替 var 进行变量声明,但会将变量的作用域限制在当前代码块中。块级作用域有以下几个特点:
- 声明变量不会提升到代码块顶部
let/const 声明并不会被提升到当前代码块的顶部,因此你需要手动将 let/const 声明放置到顶部,以便让变量在整个代码块内部可用。
function getValue(condition) {
if (condition) {
let value = "blue";
return value;
} else {
// value 在此处不可用
return null;
}
// value 在此处不可用
}
- 禁止重复声明
- 循环中的绑定块作用域的妙用
for (let i = 0; i < 10; i++) {
// ...
}
console.log(i);
// ReferenceError: i is not defined
上面代码中,计数器i只在for循环体内有效,在循环体外引用就会报错。
for循环中var和let的区别
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
上面代码中,变量i是var命令声明的,在全局范围内都有效,所以全局只有一个变量i。每一次循环,变量i的值都会发生改变,而循环内被赋给数组a的函数内部的console.log(i),里面的i指向的就是全局的i。也就是说,所有数组a的成员里面的i,指向的都是同一个i,导致运行时输出的是最后一轮的i的值,也就是 10。
如果使用let,声明的变量仅在块级作用域内有效,最后输出的是 6。
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
上面代码中,变量i是let声明的,当前的i只在本轮循环有效,所以每一次循环的i其实都是一个新的变量,所以最后输出的是6。
面试题
- 第一题
// 全局作用域
var x = 10
// 函数作用域1
function fn() {
console.log(x)
}
// 函数作用域2
function show(f) {
var x = 20
f()
}
show(fn) // 10
考点:作用域在代码写好的时候就已经确定了,并且不会改变。
解析:首先在fn函数作用域中查找,没有找到x;然后到全局作用域中查找,结果找到了x=10。
- 第二题
var fn = function() {
console.log(fn) // function() {
// console.log(fn)
// }
}
fn()
var obj = {
fn2: function() {
console.log(fn2) // ReferenceError: fn2 is not defined
}
}
obj.fn2()
解析:首先在obj.fn2函数作用域中查找,没有找到变量fn2;然后到全局作用域中查找,也没有找到fn2。注意:并不会去找obj.fn2,如果要找的是obj.fn2,应该是console.log(this.fn2)
。