5个关于JS作用域的陷阱(总结)
在 JavaScript 中,代码块、函数或模块为变量创建作用域。例如 if
代码块为变量 message
创建作用域:
if (true) { const message = 'Hello'; console.log(message); // 'Hello' } console.log(message); // throws ReferenceError
在 if
代码块作用域内可以访问 message
。但是在作用域之外,该变量不可访问。
好的,这是作用域的简短介绍。如果你想了解更多信息,建议阅读我的文章用简单的词解释 JavaScript 作用域。
以下是 5 种有趣的情况,其中 JavaScript 作用域的行为与你预期的不同。你可能会研究这些案例以提高对作用域的了解,或者只是为面试做准备。
1. for 循环内的 var 变量
思考以下代码片段:
const colors = ['red', 'blue', 'white']; for (let i = 0, var l = colors.length; i < l; i++) { console.log(colors[i]); // 'red', 'blue', 'white' } console.log(l); // ??? console.log(i); // ???
当你打印 l
和 i
变量时会发生什么?
答案
console.log(l)
输出数字 3
,而 console.log(i)
则抛出 ReferenceError
。
l
变量是使用 var
语句声明的。你可能已经知道,var
变量仅受函数体作用域限制而并非代码块。
相反,变量 i
使用 let
语句声明。因为 let
变量是块作用域的,所以 i
仅在 for
循环作用域内才可访问。
修复
把 l
声明从 var l = colors.length
改为 const l = colors.length
。现在变量 l
被封装在 for
循环体内。
2. 代码块中的函数声明
在以下代码段中:
// ES2015 env { function hello() { return 'Hello!'; } } hello(); // ???
调用 hello()
会怎样? (代码段在 ES2015 环境中执行)
答案
因为代码块为函数声明创建了作用域,所以在 ES2015 环境中调用 hello()
会引发 ReferenceError: hello is not defined
。
有趣的是,在 ES2015 之前的环境中,在执行上述代码段时不会抛出错误。 你知道为什么吗?请在下面的评论中写下你的答案!
3. 你可以在哪里导入模块?
你可以在代码块中导入模块吗?
if (true) { import { myFunc } from 'myModule'; // ??? myFunc(); }
答案
上面的脚本将触发错误: 'import' and 'export' may only appear at the top-level
。
你只能在模块文件的最顶级作用域(也称为模块作用域)中导入模块。
修复
始终从模块作用域导入模块。另外一个好的做法是将 import
语句放在源文件的开头:
import { myFunc } from 'myModule'; if (true) { myFunc(); }
ES2015 的模块系统是静态的。通过分析 JavaScript 源代码而不是执行代码来确定模块的依赖关系。所以在代码块或函数中不能包含 import
语句,因为它们是在运行时执行的。
4. 函数参数作用域
思考以下函数:
let p = 1; function myFunc(p = p + 1) { return p; } myFunc(); // ???
调用 myFunc()
会发生什么?
答案
当调用函数 myFunc()
时,将会引发错误: ReferenceError: Cannot access 'p' before initialization
。
发生这种情况是因为函数的参数具有自己的作用域(与函数作用域分开)。参数 p = p + 1
等效于 let p = p + 1
。
让我们仔细看看 p = p + 1
。
首先,定义变量 p
。然后 JavaScript 尝试评估默认值表达式 p + 1
,但此时绑定 p
已经创建但尚未初始化(不能访问外部作用域的变量 let p = 1
)。因此抛出一个错误,即在初始化之前访问了 p
。
修复
为了解决这个问题,你可以重命名变量 let p = 1
,也可以重命名功能参数 p = p + 1
。
让我们选择重命名函数参数:
let p = 1; function myFunc(q = p + 1) { return q; } myFunc(); // => 2
函数参数从 p
重命名为 q
。当调用 myFunc()
时,未指定参数,因此将参数 q
初始化为默认值 p + 1
。为了评估 p +1
,访问外部作用域的变量 p
:p +1 = 1 + 1 = 2
。
5. 函数声明与类声明
以下代码在代码块内定义了一个函数和一个类:
if (true) { function greet() { // function body } class Greeter { // class body } } greet(); // ??? new Greeter(); // ???
是否可以在块作用域之外访问 greet
和 Greeter
? (考虑 ES2015 环境)
答案
function
和 class
声明都是块作用域的。所以在代码块作用域外调用函数 greet()
和构造函数 new Greeter()
就会抛出 ReferenceError
。
6. 总结
必须注意 var
变量,因为它们是函数作用域的,即使是在代码块中定义的。
由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用 import
语法(以及 export
)。
函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。
在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。
希望这些陷阱能够帮你巩固作用域知识!
英文原文地址:https://dmitripavlutin.com/javascript-scope-gotchas/
作者:Dmitri Pavlutin
更多编程相关知识,请访问:编程入门!!
Atas ialah kandungan terperinci 5个关于JS作用域的陷阱(总结). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas





typedef struct digunakan dalam bahasa C untuk mencipta alias jenis struktur untuk memudahkan penggunaan struktur. Ia alias jenis data baharu kepada struktur sedia ada dengan menentukan alias struktur. Faedah termasuk kebolehbacaan yang dipertingkatkan, penggunaan semula kod dan pemeriksaan jenis. Nota: Struktur mesti ditakrifkan sebelum menggunakan alias itu mestilah unik dalam program dan hanya sah dalam skop di mana ia diisytiharkan.

Pengecualian nilai jangkaan pembolehubah dalam Java boleh diselesaikan dengan: memulakan pembolehubah menggunakan nilai nol menggunakan semakan dan tugasan;

Kelebihan penutupan JavaScript termasuk mengekalkan skop pembolehubah, membolehkan kod modular, pelaksanaan tertunda, dan keburukan pengendalian peristiwa termasuk kebocoran memori, peningkatan kerumitan, overhed prestasi dan kesan rantaian skop.

Arahan prapemproses #include dalam C++ memasukkan kandungan fail sumber luaran ke dalam fail sumber semasa, menyalin kandungannya ke lokasi yang sepadan dalam fail sumber semasa. Digunakan terutamanya untuk memasukkan fail pengepala yang mengandungi pengisytiharan yang diperlukan dalam kod, seperti #include <iostream> untuk memasukkan fungsi input/output standard.

Kitaran hayat penunjuk pintar C++: Penciptaan: Penunjuk pintar dicipta apabila memori diperuntukkan. Pemindahan pemilikan: Pindahkan pemilikan melalui operasi pemindahan. Keluaran: Memori dikeluarkan apabila penunjuk pintar keluar dari skop atau dikeluarkan secara eksplisit. Pemusnahan objek: Apabila objek runcing dimusnahkan, penunjuk pintar menjadi penunjuk tidak sah.

boleh. C++ membenarkan definisi dan panggilan fungsi bersarang. Fungsi luaran boleh menentukan fungsi terbina dalam, dan fungsi dalaman boleh dipanggil terus dalam skop. Fungsi bersarang meningkatkan enkapsulasi, kebolehgunaan semula dan kawalan skop. Walau bagaimanapun, fungsi dalaman tidak boleh mengakses pembolehubah tempatan fungsi luaran secara langsung, dan jenis nilai pulangan mesti konsisten dengan pengisytiharan fungsi luaran Fungsi dalaman tidak boleh rekursif sendiri.

Dalam Vue, terdapat perbezaan dalam skop apabila mengisytiharkan pembolehubah antara let dan var: Skop: var mempunyai skop global dan let mempunyai skop peringkat blok. Skop peringkat blok: var tidak mencipta skop peringkat blok, mari buat skop peringkat blok. Pengisytiharan Semula: var membenarkan pembolehubah diisytiharkan semula dalam skop yang sama, jangan biarkan.

Dalam JavaScript, jenis penunjuk ini termasuk: 1. Objek global 2. Panggilan fungsi; 4. Pengendali acara; Selain itu, penunjuk kepada ini boleh ditetapkan secara eksplisit menggunakan kaedah bind(), call(), dan apply().
