前言
本文将记录自己在学习过程中的一些理解,工作中或多或少遇到过作用域问题,但都没有深入了解,希望帮助到有同样经历的朋友,欢迎大家指正沟通交流。
脑图
什么是作用域?
定义:就是能够储存变量当中的值,并且能在之后对这个值进行访问或修改。
- 那么问题来了,这些变量的值都储存在作用域的那些地方呢?
js预编译
我们都知道,js在运行时会有三个动作,语法检测、预编译、执行代码。 那么预编译会发生什么呢?
var text = '全局变量';
function test(a) {
var private = a;
function child() {
// console.log(private);
}
child()
}
test('局部变量')
console.log(text);
- 创建GO对象(Global全局)
GO = {}
- 查找变量名做GO对象的属性名,将声明函数的函数名当做GO对象的属性名,值为函数体
GO = {
text:undefined,
test:function test() {
var private = '局部变量';
function child() {
// console.log(private);
}
child()
}
}
从这里我们可以清楚的看到,为什么在声明前打印出undefined的原因,也就是我们常说的声明提前
console.log(text);//undefined
var text='全局变量';
那么函数里的变量又是怎么储存的呢
- 函数的AO对象
1.创建AO对象:
AO={}
2.查找函数内的变量名作为AO对象的属性名,值为undefined:
AO={
private:undefined
}
3.将实参和形参统一,形参做为AO对象的属性名,实参做为值:
AO={
private:undefined,
a:'局部变量'
}
4.将声明函数的函数名当做AO对象的属性名,值为函数体:
AO={
private:undefined,
a:'局部变量',
test:function child() {
// console.log(private);
}
}
好了,讲到这里或许你对作用域还是有点模糊,没关系,看完下面你就明白了
作用域有哪几种
从预编译的过程我们知道,有全局的预编译和函数内部的预编译,实际上就是全局作用域和局部作用域
- 全局作用域
- 局部作用域(函数作用域) 先看代码
1.
var text = '全局变量';
function test() {
console.log(text);
var text='局部变量';
console.log(text);
var child='局部变量的弟弟'
}
test()
console.log(text);
console.log(child);
2.
var text = '全局变量';
function test() {
var child='局部变量的弟弟';
console.log(text);
}
test()
console.log(text);
console.log(child);
- undefined,局部变量,全局变量,child is not defined
- 全局变量,全局变量,child is not defined 代码总结:
- 作用域查找会遵循:如果函数内部AO对象没有该变量,则向GO对象查询
- 函数外不能访问函数内的变量
先看代码
var val = 1;
function test() {
console.log(val);
}
function bar() {
var val = 2;
test();
}
bar();
//打印1
代码总结:
- 变量声明在定义时就已经确定了其作用域,而不是执行时,静态作用域查找方式
同名变量与同名函数(不建议写同名)
function test() {
console.log(child)
var child = '局部变量';
console.log(child);
function child() {};
console.log(child);
}
test();
//function child(){},局部变量,局部变量
块级作用域(ES6新增)
- 块级作用域:let、const,只能在声明后的{}内访问。
- 暂时性死区:变量也会变量提升,但是在执行前不会初始化,这个阶段就叫暂时性死区。
console.log(a);//Cannot access 'a' before initialization
let a='a变量';
console.log(a);//a变量
- let、const声明的变量,除对象的{}外所有的{}都存在块级作用域
//if
let isTrue=true;
if(isTrue){
let text='{}里的变量'
};
console.log(text);//text is not defined
//for循环
for(let i=0;i<5;i++){}
console.log(i);//i is not defined
总结
想要搞懂作用域,预编译是必不可少的,代码在执行前我们就可以区分不同作用域,作用域是js的第一座大山,后面很多问题都会用到,最后感谢大家浏览。