大家好,我是小材鸟,本篇博客将带你了解JavaScript的作用域和变量提升,如果觉得本篇对你有帮助 的话,可以给博客点一个👍吗?谢谢🌹
1.什么是作用域
概念:作用域是一个变量或函数的作用范围。作用域在函数定义时,就已经确定了
目的:为了提高程序的可靠性,同时减少命名冲突
js中一共两种作用域
全局作用域:作用于整个script标签内部,或者作用于一个独立的js文件
局部作用域:作用域函数内的代码环境
全局作用域和window对象:
直接编写在script标签中的js代码,都在全局作用域。全局作用域在页面打开时创建,在页面关闭时销毁。
在全局作用域中有一个全局对象window对象的属性保存。比如在全局作用域定义一个变量,就可以用window.的形式调出来
创建的函数都会作为window对象的方法保存
全局变量
在全局作用域下声明的变量,叫[全局变量]。在全局作用域的任何一个地方,都可以访问这个变量。
在全局作用域下声明的变量是全局变量
局部变量
定义在函数作用域的变量,叫[局部变量]。仅限函数内部访问这个变量
函数的形参也是属于局部变量
从执行效率来看全局变量和局部变量:
全局变量:只有浏览器关闭时才会销毁,比较占内存
局部变量:当其所在的代码块运行结束后,就会销毁,比较节约内存。
特殊情况
无论在函数外还是内部,变量如果未经声明就赋值(如果不加var/let/const)这个变量就是全局变量
function fn(){
a = 123
}
fn();
console.log(a)//123
作用域的上下级关系:
当在函数作用域操作一个变量时,它会现在自身的作用域中寻找,如果就直接使用(就近原则)。如果没有则向上一级作用域中寻找,直到找到全局作用域;如果全局作用域中依然没有找到,则会报错ReferenceError
在函数中要访问全局变量可以使用window对象。
全局作用域的预处理:
预处理(预解析)的概念:js在解析代码之前有一个预处理(预解析)阶段,将代码中所有变量的定义和函数的定义放在所有代码的最前面这种预解析也称之为声明提前
变量的声明提前(变量提升)
使用var关键字声明的变量,会在所有代码执行之前被声明(但是不会赋值)。但如果不是var声明的变量,则变量不会被声明提前
console.log(a);//undefined
var a = 123;
foo();
function foo() {
if (false) {
var i = 123;
}
console.log(i);//undefined
}
虽然if判断中的代码不会执行但是整个代码会有解析的环节,解析的时候就已经把变量给提前声明了
函数的声明提前(函数提升)
使用函数声明的形式创建函数function show(){},会被提前声明
也就是说,整个函数会在所有代码执行之前就被创建完成。所以,在代码顺序上,我们可以先调用函数,再定义函数
注意:箭头函数不会被提前声明
fun(); // 打印 B
// 变量提升
var fun = function () {
console.log('A');
};
// 函数提升
function fun() {
console.log('B');
}
fun(); // 打印 A
//上方代码应该理解为这样
/*伪代码*/
// 函数提升
function fun() {
console.log('B');
}
var fun = undefined;
fun(); // 打印 B
fun = function () {
console.log('A');
};
fun(); // 打印A
函数在被提升时带着方法体一起提升,同时也不会被变量提升所影响,但是会被变量赋值影响
函数作用域的预处理
函数中,使用var关键字声明的变量,会在函数中所有代码执行之前就被提前声明。
函数中,没有var声明的变量都是全局变量,并且不会被提前声明
var a = 1;
function foo() {
console.log(a);
a = 2; // 此处的a相当于window.a
}
foo();
console.log(a); //打印结果是2
作用域链
只要是代码,就至少有一个作用域+
函数内部有局部作用域
如果函数内部还嵌套了函数,那么在这个作用域中就会又诞生了另一个作用域
作用域链:
在嵌套函数中,变量会从内到外逐层寻找它的定义(查找时,采用就近原则)也就是说,采用的是链式查询的方式来决定取哪个值,这种结构称之为作用域链