一、什么是作用域? 作用域是一套规则,用于确定在何处以及如何查找变量
作用域嵌套:当一个块或函数嵌套在另一个块或者函数时,就发生了作用域嵌套,因此当前作用域无法找到某个变量时,就会去外层嵌套的作用域中查询,直至找到,或者抵达全局作用域。作用域查找始终从运行时所处的最内部作用域开始,逐级向外进行,直至遇见第一个匹配的变量(遮蔽效应)
变量如何查询?
LHS查询和RHS都是引擎对变量的查询;当变量出现在赋值操作的左侧进行LHS,出现在右侧进行RHS,但是实际上RHS查询与简单的查找某个变量没有什么分别,而LHS查询将会去寻找变量的容器本身,从而可以对其赋值,从这个角度来看,RHS并不是真正意义上的“赋值的右侧”,更准确的说是“非左侧”。
LHS可以理解为——赋值操作的目标是谁,RHS可以理解为——谁是赋值操作的源头
当RHS查询在所以的嵌套作用中遍寻不到所需的变量,引擎就会抛出ReferenceError异常,相比下当LHS如果在全局作用域中也没有找到目标变量,全局作用域就会创建一个具有该名称的变量,并将其返回给引擎。但是在引入严格模式后,不会再创建一个变量,而是同样抛出一个ReferenceError异常。
接下来,如果HRS找到一个变量,但是对它进行了不合理的操作,比如对一个非函数进行函数调用,那么引擎将会抛出一个TypeError异常
ReferenceError同作用域判别失败相关,而TypeErrror则代表作用判别成功,但是对结果的操作是不合理的。
作用域有两种工作模型:词法作用域、动态作用域;
二、词法作用域:就是在写代码时将变量和块作用域写在哪里决定的,也就是说只由函数被声明时所处的位置决定。
怎样才能在运行时修改(欺骗)词法作用域呢?——欺骗词法作用域会导致性能下降
1.eval()函数:通常被用来执行动态创建的代码,下面的eval函数改变了foo的词法作用域,导致foo永远访问不到全局作用域的b
function foo (str, a){
eval(str); //欺骗
console.log(a,b);
}
var b = 2;
foo("var b = 3 ;", 1); //1, 3
2.with方法(不推荐使用)
三、函数作用域和块级作用域
函数作用域:属于这个函数的全部变量都可以在整个函数的范围内使用及复用。
区分函数声明和函数表达式的可以看function的位置,如果是声明的第一个词,就是一个函数声明,否则就是一个函数表达式。两者的区别:
1. 最重要的区别 就是他们的名称标识符将会绑定在何处。
2.函数声明存在声明提升,会被提到当前作用域的顶部,而函数表达式不会
3.以函数声明的方法定义的函数,函数可以在函数声明之前调用,而函数表达式的函数只能在声明之后调用
4.函数声明不可以省略函数名,而函数表达式可以省略,成为匿名函数表达式
函数声明
function foo(){
console.log(hello world);
}
函数表达式
var t = function () {
console.log(1);
}
匿名函数表达式
setTimeout(function(){
console.log(1);
},1000)
立即执行函数表达式(IIFE)
var a = 2;
(function IIFE(){
var a = 3;
console.log(a)//3
})()
IIFE另一个常见的用法时把他当做函数调用并传参进去。
var a = 2;
(function IIFE(global){
var a = 3;
console.log(a) ; //3
console.log(global.a) //2
})()
块作用域:最常见的作用域单元,let关键字将变量绑定到所在的任意作用域中,