JS的变量提升和函数提升,函数声明与函数表达式

核心结论:

变量提升和函数提升是JavaScript在编译阶段将声明移动到当前作用域顶部的机制。其中函数提升只发生在函数声明上,且优先级高于变量提升;而函数表达式(包括箭头函数)不存在函数提升。

一、函数声明 vs 函数表达式

1. 函数声明(Function Declaration)

function sum(a, b) {
  return a + b;
}

2. 函数表达式(Function Expression)

// 方式1:匿名函数赋值给变量
const multiply = function(a, b) {
  return a * b;
};

// 方式2:具名函数表达式(函数名只在函数内部可用)
const divide = function div(a, b) {
  return a / b;
};

// 方式3:箭头函数(ES6,属于函数表达式的一种)
const subtract = (a, b) => a - b;
维度函数声明函数表达式
提升行为函数体完整提升只提升变量名(值仍是 undefined
调用时机可在声明前调用只能在声明后调用
语法形式function name() {}const/let/var name = function() {}
是否匿名必须有函数名可以匿名
块级作用域不遵守(会提升到函数/全局)遵守(let/const 块级)

二、变量提升

什么是变量提升?
变量提升是指 var 声明的变量在编译阶段被提升到作用域顶部,初始化为 undefined,赋值留在原地。

console.log(a);  // undefined(不会报错)
var a = 10;
console.log(a);  // 10

// 解释:上面代码实际执行顺序:
var a;           // 提升到顶部,初始化为 undefined
console.log(a);  // undefined
a = 10;          // 赋值留在原地
console.log(a);  // 10

可以简单理解,变量提升相当于把声明并赋值的语句,拆分为声明变量和赋值操作,并给变量一个初始值:undefined。

更专业的说法是:使用 var 声明的变量,会在编译阶段被提升到当前作用域的顶部,但赋值操作留在原地。初始值为 undefined

let 和 const 也存在声明提升,但与 var 不同,它们不会被初始化为 undefined。从进入作用域到声明语句之间,变量处于暂时性死区(Temporal Dead Zone, TDZ),此处访问会抛出 ReferenceError,这更符合现代 JavaScript 开发的最佳实践。

console.log(b);  // ❌ ReferenceError: Cannot access 'b' before initialization
let b = 20;

解释完变量提升我们来看几道题:

问题一:

(function() {
    var x=foo();
    var foo=function foo() {
        return "foobar"
    };
    return x;
})();

思考一下,上一段代码运行后输出?

问题二:

(function() {
    var x = foo();          
    function foo() {       
        return "foobar"
    };
    return x;              
})();

思考一下,上一段代码运行后输出?

先来看问题一,var会变量提升,所以代码段等价于

(function(){
    var x;        //undefined
    var foo;    //undefined
    x=foo();    //注意:// 此时foo还是undefined
    foo=function foo(){
        return "footbar"
    };
    return x;
})();

当程序执行到x=foo()时,foo还是undefined,而undefined不是函数,因此尝试执行undefined(),会抛出TypeError的错误。如:

因此问题一的代码并不能顺利运行,会抛出”类型错误“的错误。

再来看看问题二,var会变量提升,所以代码段等价于

(function() {
    var x;        //undefined
    x = foo();          //可以直接调用
    function foo() {       //函数声明会完整提升
        return "foobar"
    };    
    return x;              //返回“footbar"
})();

当程序执行到x=foo()时,此时foo是一个函数声明,可以整体提升(涉及到函数提升了),立即执行foo,返回”footbar"。

三、函数提升

学习函数提升之前需要弄清”函数声明“和”函数表达式“的定义。(前面讲过了,这里不再赘述)

函数提升只发生在【函数声明】上,【函数表达式】不存在真正意义上的函数提升。

函数声明 --> 有提升

foo();    //打印 “声明提升”

function foo(){
    console.log("声明提升");
}

函数表达式 --> 无函数提升 

bar();    //TypeError:bar is not a function

var bar=function (){
    console.log("表达式");
};

函数表达式就是赋值函数,所以这里本质遵循var的变量提升,提升的是var bar,并给初始值为undefined。bar()会尝试执行undefined(),抛出TypeError的错误。

在看看下面的代码段,运行之后输出的是?

console.log(typeof foo);    //打印foo的类型
console.log(foo);

var foo = "hello";

function foo() {
  return "world";
}

该代码段既有变量提升,又有函数提升,我们来看看实际运行结果:

这是因为 当同名时,函数提升的优先级高于变量提升!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值