JS执行
js为解析型语言,执行分两步:
- 预编译阶段:var变量、function函数被存储起来,变量优先函数声明,但函数是预编译阶段赋值
- 执行阶段:运行到当前行,从变量对象中找到并赋值
函数
f()
function f(){
console.log(1)
}
// 函数申明提前被赋值,等价于👇
function f(){}
f()
函数表达式
// 函数表达式的声明会被提前
console.log(num)// undefined
num() // 报错 TypeError
/**
* 因为变量提升并分配了作用域,所以不是ReferenceError
* 但此时没有赋值,调用undefined导致非法调用,抛出TypeError
**/
var num = function (){
console.log(1);
}
num();//1
console.log(num)//函数本身
变量
var
(function(){
console.log(a);//undefined
var a ="小旋风";
console.log(a);//小旋风
}())
/**
* 声明提前了,只是没有赋值,赋值仍保留远处不变
**/
👇等价于
(function(){
var a;
console.log(a);//声明了但未赋值,所以输出undefined;
a ="小旋风";
console.log(a);//上一步赋值了,所以输出小旋风
}())
let、const
// let、const 初始化它们之前,它们是不可访问的
console.log(bar); // 报错ReferenceError
let bar = 2;
console.log(foo); // 报错ReferenceError
const foo = 2;
let变量如果不存在变量提升,console.log(name)就会输出ConardLi,结果却抛出了ReferenceError
说明了,let也存在变量提升,但是它存在一个“暂时死区”,在变量未初始化或赋值前不允许访问
在“暂时死区”,即使使用typeof也会触发引用错误,只有执行过变量声明语句后,变量才会从“暂时死区”移除
if(condition){
console.log(typeof value); // 报错ReferenceError
let value= '孙悟空';
}
在 let作用域外访问则不会报错
console.log(typeof value); // undefined
if(condition){
let value= '孙悟空';
}
const声明对象
const 声明不允许修改绑定,语训修改绑定的值
const person = {
name: '孙悟空'
}
// 可以修改对象属性
person.name = '小旋风'
// 跑出错误
person = {
name: '小旋风'
}
变量重复定义
function foo() {
var x = 1;
function bar() {
var x = 'A';
console.log('x in bar() = ' + x); // 'A'
}
console.log('x in foo() = ' + x); // 1
bar();
}
foo();
/**
* 查找变量时从自身函数定义开始,从“内”向“外”查找
* 内部了与外部名的变量重,则内部变量将“屏蔽”外部变量
**/
👇等价于
x in foo() = 1
x in bar() = A
let 、const不会全局创建属性
let bar = 2;
const foo = 2;
// 另外 let和const声明的变量不会挂载到 window
console.log
window.bar// undefined
window.foo// undefined
函数、变量声明顺序
函数优先提升,然后才是变量
function f(){
var a = 10
console.log(a)
}
f() // 10
var f = 10
f() // 报错 TypeError
console.log(a); // 函数本身
var a = "孙悟空";
function a(){ console.log("小旋风"); }
console.log(a); // 孙悟空
/**
* 变量a在提前,a未赋值,覆盖了上面声明的函数a
**/
👇等价于
function a(){
console.log("小旋风");
}
var a; // 由于上面函数已声明a,相同的变量名声明会被直接忽略
console.log(a) // 输出函数本体
a = "孙悟空"
变量重复定义 & 优先级
foo() // 1
var foo
function foo(){
console.log(1)
}
foo = function(){
console.log(2)
}
/**
* var foo 尽管在出现在 function foo()前,但是它是重复声明,所以被忽略
**/
👇等价于
function foo(){
console.log(1)
}
foo() // 1
foo = function(){
console.log(2)
}
总结
- let、const 的【创建】过程被提升了,但是初始化没有提升
- var 的【创建】和【初始化】都被提升了
- function 的【创建】【初始化】和【赋值】都被提升了
异常
- ReferenceError:作用域判别失败
- TypeError:作用域判别成功,但操作非法
function foo(a){
console.log(a+b)
b=a
}
foo(2)
// ReferenceError: b is not defined
RHS查询下,在所有作用域找不到变量,引擎抛出ReferenceError
LHS & 非严格模式,会自动在**全局**下创建变量
function foo(a){
b=a
console.log(a+b)
}
foo(2) // 4
console.log(b) // 2
【多多登场】