声明提升

422 阅读3分钟

JS执行

js为解析型语言,执行分两步:

  1. 预编译阶段:var变量、function函数被存储起来,变量优先函数声明,但函数是预编译阶段赋值
  2. 执行阶段:运行到当前行,从变量对象中找到并赋值

函数

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

letconst不会全局创建属性

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



【多多登场】

开熏