JS专精-变量提升/函数

207 阅读4分钟

两种函数

  • 编程的函数:执行某个任务的一段代码

  • 数学的函数:f(x) = y
    假设输入为x1,那么必有一个输出y1: f(x1)=y1
    假设输入为x2,那么对应的输出可以是y1,也可不是,不管你输入多少次x1,输出都是y1

  • 如果你的函数符合数学定义那么就叫做函数式

函数的影响因素

重点:所有函数的问题都可以归结到这两个因素上

  • 定义时输入的参数 params
  • 定义时的环境 env

变量提升

JS的六种声明变量的方式

  • var
  • function 函数声明和函数表达式
  • let
  • const
  • import
  • class 类声明和类表达式

function声明提升

helloWorld();  // 打印 'Hello World!'
function helloWorld(){
  console.log('Hello World!');
}

函数声明会在编译阶段就会被记录在词法环境中并且保存在内存中,因此我们可以在函数进行实际声明之前对该函数进行访问。在词法环境中被保存为函数 上面函数声明保存在词法环境中像下面这样:

lexicalEnvironment = {
  helloWorld: < func >
}

所以在代码执行阶段,当Javascript引擎碰到helloWorld()这行代码,会在词法环境中寻找,然后找到这个函数并执行它。

函数表达式声明

只有函数声明才会被直接提升,使用函数表达式声明的函数不会被提升

helloWorld();  // TypeError: helloWorld is not a function
var helloWorld = function(){
  console.log('Hello World!');
}

使用var声明的helloWorld是个变量,并不是函数,Javascript引擎只会把它当成普通的变量来处理,而不会在词法环境中给它赋值。在词法环境中被保存为普通变量 保存在词法环境中像下面这样:

lexicalEnvironment = {
  helloWorld: undefined
}

var变量提升

所谓的声明提升只是在编译阶段Javascript引擎将函数声明变量声明存储在词法环境中,但不会给它们赋值。等到了执行阶段,真正执行到赋值那一行的时候,词法环境才会更新。

let和const变量提升

在Javascript中所有声明的变量(var,const,let,function,class)都存在变量提升的情况。使用var声明的变量,在词法环境中会被初始化为undefined,但用let和const声明的变量并不会被初始化。 使用let和const声明的变量只有在执行到赋值那行代码的时候才会真正给他赋值,这也意味着在执行到变量声明的那行代码之前访问那个变量都会报错,这就是我们常说的暂时性死区(TDZ)。即在变量声明之前都不能对变量进行访问。 在代码编译阶段,Javascript引擎会把变量a存储在词法环境中,并把a保持在未初始化的状态。此时词法环境像下面这样:

lexicalEnvironment = {
  a: <uninitialized>
}

这里需要纠正一个误区,就是let和const声明的变量只有暂时性死区,不存在变量提升,其实是不对的,举个例子证明理解一下:

let a = 1;
{
  console.log(a);
  let a = 2;
}

上面的代码会被报错:如果不存在变量提升,理论上不会报错。

class声明提升

与let、const类似,使用class声明的类也会被提升,然后这个类声明会被保存在词法环境中但处于未初始化的状态,直到执行到变量赋值那一行代码,才会被初始化。所以,class声明的类一样存在暂时性死区(TDZ)。
函数表达式还是类表达式遵循的规则和变量声明是一样的。

变量提升小结

  • JS声明都存在变量提升
  • var:存在变量提升,在编译阶段会被初始化为undefined;
  • let:存在变量提升,存在暂时性死区(TDZ),执行阶段,如果没赋值,则初始化为undefined
  • const:存在变量提升,存在暂时性死区(TDZ),如果没有赋值,编译阶段就会报错
  • let 和 const 分为创建/初始化/赋值,三阶段,var是创建和初始化一起提升,而这两个只提升了创建。
  • class: 存在变量提升,存在暂时性死区(TDZ);
  • function:存在变量提升,在变量声明之前可以访问并执行;这个是最特殊的,在声明之前使用可以不损失任何功能至少 var 之前使用变量会变成undefined。因为它在词法作用域里就被声明成函数,之后调用直接在词法作用域里查找即可