Javascript 中的提升

Posted by cl9000 on October 07, 2020

不畏惧失败是创造力的一个基本要素。——<艾尔文·兰德博士>

介绍

提升是一种JavaScript行为,通常是在给变量赋值或定义函数之前,让变量和函数可用。实际上,它在执行之前将变量、函数和类声明置于其作用域(全局作用域或函数)的顶部。

实际上,JavaScript不会移动或添加代码来提升声明。这些声明在解释器的编译阶段被放入内存中——使它们在代码执行之前可用。

在本文中,我们将学习提升以及它如何影响变量、函数和类。

变量提升

提升的一个关键方面是将声明放入内存——而不是值。

让我们来看一个例子:

1
2
3
console.log(name); // Prints undefined, as only declaration was hoisted
var name = "John";
console.log(name); // Prints "John"

这是因为JavaScript看到我们在作用域中有一个变量名,并将其放入内存。用var声明的变量在被赋值之前都是未定义的。

使用 let 和 const 进行变量提升

JavaScript 开发人员很少使用 ECMAScript 2015(通常称为 ES6)中引入 var letconst关键字。用 letconst声明的变量被提升。但是,它们没有使用undefined、 或任何值进行初始化。因此,如果在它们初始化之前使用它们,我们将得到一个ReferenceError.

让我们重新使用相同的示例,但使用 let 代替 var

1
2
3
console.log(name); // Uncaught ReferenceError: Cannot access 'name' before initialization
let name = "John";
console.log(name);

上面的代码 ReferenceError在第一行抛出 a并结束执行。

添加 const 关键字是为了在 JavaScript 中引入不可变值 - 初始化后无法更改的值。因此,在使用 const时,我们必须声明变量并为其赋值。如果我们不这样做,我们会得到一个SyntaxError:

1
2
console.log(name); // Uncaught SyntaxError: Missing initializer in const declaration
const name;

就像let,ReferenceError当我们在初始化之前尝试使用常量时,我们会得到一个:

1
2
3
console.log(name); // Uncaught ReferenceError: Cannot access 'name' before initialization
const name = "John";
console.log(name);

函数提升

函数声明在 JavaScript 中被提升。函数声明以关键字function,然后是它的名称和括号中的参数,然后是它的主体。让我们考虑以下代码:

1
2
3
4
5
6
7
8
9
let first_name = "Stack";
let last_name = "Abuse";

let result = concat(first_name, last_name);
console.log(result); // StackAbuse

function concat(x, y) {
return x + y;
}

与变量一样,JavaScript 在执行该范围内的代码之前将函数放入内存中。因此,提升允许我们 concat()在稍后在代码中定义函数之前调用该函数

虽然提升了函数声明,但函数表达式的工作方式不同。函数表达式是当我们将变量分配给函数时。例如,下面的代码将返回一个错误:

1
2
3
4
5
func_express(); // TypeError: func_express is not a function

var func_express = function () {
console.log('This the function expression is not a function');
};

使用箭头函数的函数提升

ECMA2015 引入了一种创建匿名函数的新方法,即箭头函数。它们由一对包含 0 个或多个参数的方括号、一个箭头 =>和大括号中的函数体定义。对于起重,它们的操作与其他功能一样。例如:

1
2
3
4
5
arrow_func_express(); // TypeError: arrow_func_express is not a function

var arrow_func_express = () => {
console.log('This the arrow function is not a function');
};

当使用箭头函数或任何其他函数表达式时,我们必须始终在我们的代码中使用它之前定义该函数。这是使用函数表达式的正确方法:

1
2
3
4
5
let arrow_func_express = () => {
console.log('This function expression will work');
};

arrow_func_express(); // This function expression will work

类提升

在JavaScript中,类声明会被升起。升起时,类声明未初始化。这意味着,虽然JavaScript可以找到我们创建的类的引用,但在代码中定义该类之前,它不能使用该类。

让我们看看下面的例子,如果试图在类定义之前访问它,就会抛出一个错误:

1
2
3
4
5
6
7
8
9
10
11
var person = new Person();
person.name = "Jane";
person.age = 25;

console.log(person); // Uncaught ReferenceError: Cannot access 'Person' before initialization"

class Person {
constructor(name, age) {
this.name = name; this.age = age;
}
}

ReferenceError类似于在脚本中初始化之前尝试访问用 letconst声明的变量时所发生的情况。

类表达式(将类定义赋给变量)的行为类似于函数表达式。他们的声明被升起,但没有他们的分配价值。

让我们以前面的例子为例,改用类表达式:

1
2
3
4
5
6
7
8
9
10
11
12
var person = new Person();
person.name = "Jane";
person.age = 25;

console.log(person); // Uncaught TypeError: Person is not a constructor"

var Person = class {
constructor(name, age) {
this.name = name;
this.age = age;
}
}

当变量 Person 被提升时,它被赋予值undefined。由于我们不能将undefined值用作类,因此 JavaScript 会抛出一个TypeError.

在使用类声明或类表达式时,我们应该始终在代码中较早地定义类以使用它。正确使用类的方法如下:

1
2
3
4
5
6
7
8
9
10
11
class Person {
constructor(name, age) {
this.name = name; this.age = age;
}
}

var person = new Person();
person.name = "Jane";
person.age = 25;

console.log(stack); // Person { name: 'Jane', age: 25 }

结论

JavaScript 提升、变量、函数和类声明。那篇文章展示了提升对我们如何编写 JavaScript 代码的影响。

由于提升,可以在定义之前使用函数声明。然而,为了最大限度地减少获取undefined值以及引用或类型错误的机会,在我们的代码中定义变量、函数表达式和类后使用它们会更安全。

参考

关注【公众号】,了解更多。



支付宝打赏 微信打赏

赞赏一下 坚持原创技术分享,您的支持将鼓励我继续创作!