前言
当每一个程序员小白刚深入了解JavaScript,就会遇到被誉为非常重要但难以掌握的知识——闭包 不过在开始学习闭包前,就不得不提到作用域与词法作用域,这两个基础知识是了解闭包的前置条件,所以我们先来了解作用域和词法作用域。
作用域
我们首先要了解,词法作用域是作用域的一种规则体系,所以没有作用域就没有词法作用域,我们由浅入深。
什么是作用域
作用域规定了在代码的哪个部分可以访问或查找特定的标识符。根据定义的宽泛程度和规则,作用域可以分为全局作用域、局部作用域、块级作用域等不同类别。
全局作用域,函数作用域
全局作用域和局部(函数)作用域在这篇文章有详细介绍 juejin.cn/post/737240…
块级作用域
块级作用域主要是使用let 与 const 来使用
我们演示一段
var arr=[];
for(var i = 0; i <10; i++) {
arr[i]=function(){
console.log(i);
}
}
arr.forEach(function(item){
item()
})
这段代码在for循环下,会输出什么?
10个10
但是在我们的眼里,这段代码的输出应该是0~9,这就是全局作用域的效果
,要是想让这段代码输出我们想要的结果
试试把for循环中的var变量改成let,我们再试试
var arr=[];
for(let i = 0; i <10; i++) {
arr[i]=function(){
console.log(i);
}
}
arr.forEach(function(item){
item()
})
这就是用let关键字,将i变量绑定到for循环所在的快作用域内
而const则是用来创造块作用域变量,但值是定值,不像let可以是变量
var foo =ture;
if(foo){
var a =2;
const b =3;//包含在if中的块作用域常量
a =3;//正常
b=4;//错误
}
console.log(a);//3
console.log(b);//ReferenceError
作用域链
js引擎在查找变量时会先在函数中寻找,找不到就会根据outer的指向去到外层作用域中查找,层层往上,这种查找的关系链就称为 作用域链
function bar(){
var myname ='Tom'
let test1 = 100
if(1){
let myname = 'Jerry'
console.log(test, myname);
}
}
function foo(){
var myname = '彭于晏'
let test = 2
{
let test = 3
bar()
}
}
var myname = 'john'
let test = 1
foo()
这里的输出值就是 1和Jerry
词法作用域
词法作用域由代码的物理布局或书写位置决定,即在编写代码时变量和函数的定义位置决定了其作用域。这意味着作用域在编译阶段就已经固定下来,不会在运行时改变。编译器或解释器在代码执行前的词法分析阶段,就能确定每个标识符的有效范围。
也就是说词法作用域是在变量声明的时候作用域就已经确定了,是静态的。 “编译器在当前作用域中声明一个变量name”,这个应该就是在执行上下文创建阶段生成VO变量对象时会有变量提升和函数提升的操作,var定义的变量会初始为undefined
其中eval()与with(){}方法可以欺骗词法作用域
function foo(a){
eval(str)
console.log(a,b);
}
foo(1,'var b = 2')
这段代码没有声明b,但在输出时,会输出b的值,eval可以直接使用str
var obj ={
a:1,
b:2,
c:3
}
whit(obj){
a = 3
b = 4
c = 5
}
console.log(obj);
这段代码输出时,a,b,c的值会变成3,4,5而不是2,3,4
结语
这就是作用域了,了解完这个概念,就可以开始尝试闭包了