前言
作用域大家都不陌生,正所谓面试造火箭,现实拧螺丝,面试时我们会碰到各种类型的题,这些题跟作用域的特性息息相关,这种体现理论的题最容易犯错,往往看了解析会恍然大悟。本节我们就来学习一下作用域的特征。
作用域特征
作用域包含几个特征,掌握了它们,相关面试题也就很简单了:
- 块级作用域。块级作用域简单来说就是{}里面的区域,es6中的let与const声明的变量就是块级作用域,块级作用域的变量只会在块级里面生效。
js
function a(){
let b = 1
}
console.log(b) //b is not defined
b是块级作用域,在块级外就是未定义。除了let、const声明出来的变量,在js中只有函数才有块级作用域。上述例子中我们用let定义了b变量,如果换成var呢?
js
function a(){
var b = 1
}
console.log(b) //b is not defined
依旧是未定义的,因为b是在a函数里面的定义的,它就是块级作用域。除此之外在其他地方如果用var它就是全局的。
js
if(true){
var a = 0
}
console.log(a) //0
这时候把var换成let或const,那么a肯定就是未定义的,因为这两个声明出来的变量是块级的。三者之间的区别我们要牢牢掌握的。
- 作用域链。函数中的变量值如果当前函数未定义,会去上一层函数中去寻找,直至找到或者到script全局,这就是作用域链,作用链讲究就近原则 ,并且只有从内往外找,外部不能访问内部的变量。
js
function a(){
var c = 1
function b(){
var d = 2
console.log(c) // 1
}
b()
console.log(d) // d is not defined
}
a()
- 全局变量。如果一个变量直接赋值,没有声明那么它就是全局变量,有着全局作用域,全局变量就是在window上面的。
js
(function () {
var a = b = 1;
})();
// console.lg(a); // a is not defined
console.log(b); // 1
b是直接赋值的,所以b是全局变量,相当于window.b,而a是在函数中定义的,所以a是块级作用域,在函数外打印不出,b却可以。
- 变量提升。用var声明的变量,会把声明放到最顶层,这种机制被称为变量提升,注意let、const是没有变量提升的。
js
console.log(a) // undefined
var a = 1
console.log(b) //Cannot access 'b' before initialization
let b = 2
a用var声明的,var就会放到前面,所以打印undefined而没有报错,但是let就会直接报错。
实例
熟悉以上特性,碰到相关面试题做出来就不在话下,下面我们实战解析,看下具体的,面试题:
- 第一道:
js
function a() {
var b = 1;
function c() {
console.log(b);
var b = 2;
console.log(b);
}
c();
console.log(b);
}
a();
打印出来的依次是undefined、2、1。这道题考察了变量提升、作用域链,在c函数中变量b进行了声明提升,所以第一次打印b为undefined,后面赋值为2,而a函数中,有声明的b,根据作用域链打印出来为1。
- 第二道:
js
function a(b) {
console.log(b);
function b() {}
console.log(b);
}
a(2);
这道题打印出来的是两个b函数,该题考查了变量的优先级,我们在函数中声明了b函数,声明函数提升,所以函数中打印的都是b函数。下面我们将b赋值为函数又会打印什么呢?
js
function a(b) {
console.log(b);
var b = function () {};
console.log(b);
}
a(2);
答案是2,赋值的函数,注意这次我们是将b进行赋值,而不是直接声明b函数,那为什么第一个不是undefined呢?因为第一个b表示了形参 ,当我们直接a(2),此时b就是实参的值。通过这两个例子我们知道变量是有优先级,具体的优先级为:
变量声明>函数声明>参数>变量提升
在函数中碰到相同的变量,我们要根据优先级知道它表示的是哪个值。
总结
作用域考查的就是我们的基础,面试中碰到了作用域相关的题,我们可以按照以下步骤去分析:
-
变量是否为全局变量、是否在函数内声明。
-
找到变量作用域链。
-
考虑变量表示的优先级。
通过以上分析,大概率就能得到正确的答案啦。