目录
变量提升/预定义
function和var
重名:函数覆盖变量
不执行代码
重复声明:只提升一次
函数形参:变量提升
带 var 和不带 var
全局作用域: window 的属性
私有/函数作用域:
带 var 的是私有变量
IIFE 函数具备自己的作用域,所以全局下不会变量提升
匿名自执行函数在自己的作用域内存在正常的变量提升
非匿名自执行函数的函数名在自己的作用域内变量提升,且修改函数名的值无效
不带 var 的是会沿着作用域链查找
var a=b=1 等价于var a=1;b=1
let、const 、class的暂时性死区
顶级作用域和函数作用域:let 不会在全局对象上创建属性
let不允许重复声明
*编译
分词/词法分析:分解(var a=2=> var,a,=,2)
解析/语法分析:词法数组=>程序语法 结构的“抽象语法树”(a->2)(Abstract Syntax Tree,AST)
代码生成:将 AST 转换为可执行代码
变量提升/
预定义
变量提升是当栈内存作用域形成时,JS执行前,浏览器会将带有var, function
关键字的变量提前进行声明 declare(值默认就是 undefined),定义 defined(就是赋值操作),这种预先处理的机制就叫做变量提升机制也叫预定义。
JS 运行机制基础版
function和var
print()
function print(){
console.log('林一一')
}
print()
print()
var print = function() {
console.log('林一一')
}
print()
/*输出
Uncaught TypeError: print is not a function
/
重名:函数覆盖变量
console.log(a);
var a=1;
function a(){
console.log(1);
}
// 或
console.log(a);
function a(){
console.log(1);
}
var a=1;
// 输出都是: ƒ a(){ console.log(1);}
不执行代码
console.log(a)
if(false){
var a = '林一一'
}
console.log(a)
/* 输出
undefined
undefined
/
重复声明:只提升一次
var a = 1;
function foo(a) {
console.log(a)
var a
console.log(a)
}
foo(a);
// 输出 1 1
函数形参:变量提升
function a(b){
console.log(b);
}
a(45);
// 等价于
// function a(b) {
// var b = undefined;
// b = 45;
// }
带 var 和不带 var
全局作用域: window 的属性
私有/函数作用域:
带 var
的是私有变量
IIFE 函数具备自己的作用域,所以全局下不会变量提升
var a = 10;
(function c(){
})()
console.log(c)
// Uncaught ReferenceError: c is not defined
匿名自执行函数在自己的作用域内存在正常的变量提升
var a = 10;
(function(){
console.log(a)
a = 20
console.log(a)
})()
console.log(a)
// 10, 20, 20
非匿名自执行函数的函数名在自己的作用域内变量提升,且修改函数名的值无效
var a = 10;
(function a(){
console.log(a)
a = 20
console.log(a)
})()
// ƒ a(){a = 20 console.log(a)} ƒ a(){a = 20 console.log(a)}
var foo = '林一一';
(function(f){
console.log(foo);
})(foo);
// undefined
var foo = '林一一';
(function(f){
console.log(foo);
var foo = f || 'hello';
})(foo);
//undefined
var a = 10;
console.log(window.a);
(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)
})()
//undefined undefined undefined 20
不带 var
的是会沿着作用域链
查找
function foo(){
console.log(a)
a =12;
b = '林一一'
console.log('b' in window)
console.log(a, b)
}
foo()
/* 输出
Uncaught ReferenceError: a is not defined
/
var a=b=1 等价于var a=1;b=1
// 1
console.log(a, b)
var a =12, b ='林一一'
function foo(){
// 2
console.log(a, b)
// 3
var a = b =13
console.log(a, b)
}
foo()
console.log(a, b)
/* 输出:
undefined undefined
undefined "林一一"
13 13
12 13
*/
1处的 a, b 其实就是 window下面的属性为 undefined。在函数内部由于变量提升机制 a
带 var
一开始就是 undefined,b
不带var
将向上级作用域查找,找到全局作用域下的林一一
所以2处打印出来的就是 undefined "林一一"
。随后 a =13,window.b =13
,即原来 b='林一一'
变成了 b=13
,打印出13, 13
,最后第4处打印处12, 13
。
let
、const
、class的
暂时性死区
“暂时性”区:这个区域取决于代码执行的时间点,而不是代码编写的顺序。
{
// 暂时性死区始于作用域开头
const func = () => console.log(letVar); // 没问题
// 在暂时性死区内访问 letVar 会抛出 `ReferenceError`
let letVar = 3; // 暂时性死区结束(对 letVar 而言)
func(); // 在暂时性死区外调用
}
用 let
、const
或 class
声明的变量可以称其从代码块的开始一直到代码执行到变量声明的位置并被初始化前,都处于一个“暂时性死区”(Temporal dead zone,TDZ)中。
当变量处于暂时性死区之中时,其尚未被初始化,并且任何访问其的尝试都将导致抛出 ReferenceError。
当代码执行到变量被声明的位置时,变量会被初始化为一个值。如果变量声明中未指定初始值,则变量将被初始化为 undefined
。
这与 var 声明的变量不同,如果在声明位置前访问 var
声明的变量会返回 undefined
。
{
// 暂时性死区始于作用域开头
console.log(bar); // undefined
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
var bar = 1;
let foo = 2; // 暂时性死区结束(对 foo 而言)
}
顶级作用域和函数作用域:let
不会在全局对象上创建属性
var x = "global";
let y = "global";
console.log(this.x); // "global"
console.log(this.y); // undefined
let不允许重复声明
let x = 1;
{
var x = 2; // 重复声明的 SyntaxError
}
参考链接:彻底解决 JS 变量提升| 一题一图,超详细包教包会😉 - 掘金