Trong bài viết này, bạn sẽ tìm hiểu về phạm vi và cách hoạt động của phạm vi trong JavaScript.
Phạm vi là một khái niệm cơ bản trong JavaScript và các ngôn ngữ lập trình khác. Phạm vi là một khái niệm xác định ngữ cảnh mà các biến được truy cập và sử dụng. Điều này sẽ trở nên hữu ích và dễ áp dụng hơn cho mã của bạn khi bạn tiếp tục tìm hiểu JavaScript và làm việc nhiều hơn với các biến.
Phạm vi có thể giúp bạn:
- Sử dụng bộ nhớ hiệu quả hơn: Phạm vi cho phép tải các biến chỉ khi cần. Nếu một biến nằm ngoài phạm vi thì bạn không cần cung cấp biến đó cho mã đang thực thi.
- Tìm và sửa lỗi dễ dàng hơn: Việc tách biệt các biến với phạm vi cục bộ giúp bạn dễ dàng khắc phục lỗi trong mã hơn vì không giống như biến toàn cục, bạn có thể tin tưởng rằng mã từ phạm vi bên ngoài không thể thao túng các biến có phạm vi cục bộ.
- Tạo các khối mã nhỏ có thể sử dụng lại: Ví dụ: bạn có thể viết một hàm thuần tuý không dựa vào phạm vi bên ngoài. Bạn có thể dễ dàng di chuyển một hàm như vậy sang nơi khác mà không cần thay đổi gì nhiều.
Phạm vi là gì?
Phạm vi của biến xác định vị trí mà bạn có thể sử dụng biến trong mã.
JavaScript xác định các biến thuộc phạm vi toàn cầu hoặc cục bộ:
- Các biến có phạm vi toàn cục có sẵn từ tất cả các phạm vi khác trong mã JavaScript.
- Các biến có phạm vi cục bộ chỉ dùng được trong ngữ cảnh cục bộ cụ thể và được tạo bằng các từ khoá, chẳng hạn như
var
,let
vàconst
. Nếu bạn sử dụng các từ khoávar
,let
hoặcconst
để tạo một biến trong một hàm, thì biến đó có phạm vi cục bộ.
Các phần sau trong bài viết này thảo luận về phạm vi khối và từ vựng:
- Biến Phạm vi khối có sẵn cục bộ cho một khối được xác định theo vị trí của dấu ngoặc nhọn được xác định trong câu lệnh khối. Chỉ các biến được khai báo bằng từ khoá
let
hoặcconst
mới có phạm vi chặn. - Phạm vi phạm vi sử dụng vị trí mà một biến được khai báo trong mã nguồn để xác định nơi biến đó có sẵn. Bạn sử dụng hàm đóng để cấp cho một hàm kèm theo quyền truy cập vào các biến được tham chiếu ở phạm vi bên ngoài được gọi là môi trường từ vựng.
Khi một biến được truy cập trong phạm vi của biến, JavaScript sẽ trả về giá trị được chỉ định hoặc tạo ra lỗi.
Cách khai báo một biến:
- Dùng các từ khoá
var
,const
hoặclet
để khai báo các biến phạm vi cục bộ hoặc toàn cục. - Dùng từ khoá
const
hoặclet
để khai báo các biến phạm vi khối.
Khi bạn khai báo biến var
trong một hàm, nội dung khai báo sẽ cung cấp biến đó cho hàm bao quanh gần nhất. Bạn không thể dùng từ khoá var
để khai báo các biến có phạm vi khối.
Ví dụ về phạm vi
Ví dụ này minh hoạ phạm vi toàn cục vì biến greeting
được khai báo bên ngoài hàm hoặc khối bất kỳ. Điều này giúp tất cả mã trong tài liệu hiện tại có thể sử dụng giá trị của biến này:
const greeting = 'hello';
console.log(greeting); // 'hello'
Trong ví dụ về phạm vi toàn cầu, biến greeting
được gán giá trị hello
.
Ví dụ này minh hoạ phạm vi cục bộ vì khai báo biến greeting
bằng từ khoá let
trong một hàm. Biến greeting
là một biến có phạm vi cục bộ và không sử dụng được bên ngoài hàm.
function greet() {
let greeting = 'Hello World!';
console.log(greeting);
}
Ví dụ này minh hoạ phạm vi khối vì khai báo biến greeting
trong một khối để biến chỉ truy cập được trong dấu ngoặc nhọn:
if (true) {
const greeting = 'hello';
}
console.log(greeting); // ReferenceError: greeting is not defined
Lưu ý khi hàm console.log
cố gắng xuất giá trị của biến greeting
, JavaScript sẽ trả về thông báo lỗi ReferenceError
thay vì thông báo hello
như dự kiến. Tại sao?
Hệ thống trả về lỗi vì biến greeting
có phạm vi khối và khối gần nhất là một phần của câu lệnh có điều kiện if
. Bạn không thể truy cập vào các biến let
và const
mà bạn khai báo bên trong một khối từ bên ngoài khối đó. Do đó, bạn chỉ có thể truy cập vào biến greeting
trong dấu ngoặc nhọn. Biến này chỉ định phạm vi khối.
Ví dụ này sửa lỗi vì di chuyển phương thức console.log(message)
bên trong các dấu ngoặc nhọn. Mã đã cập nhật sẽ chuyển phương thức console.log(message)
vào trong khối.
if (true) {
const greeting = 'hello';
console.log(greeting);
}
Loại phạm vi
Phạm vi toàn cầu
Bạn có thể truy cập vào các biến có phạm vi toàn cục từ bất kỳ đâu trong chương trình.
Hãy xem xét tệp HTML nhập hai tệp JavaScript: file-1.js
và file-2.js
:
<script src="file-1.js"></script>
<script src="file-2.js"></script>
Trong ví dụ này, biến globalMessage
có phạm vi toàn cục và được viết bên ngoài một hàm. Trong khi chạy và thực thi, bạn có thể truy cập vào giá trị của biến globalMessage
từ bất kỳ vị trí nào trong chương trình JavaScript.
Bạn có thể xem nội dung của tệp file-1.js
và file-2.js
trong đoạn mã này. Hãy lưu ý sự sẵn có của biến globalMessage
trong cả hai tệp.
// file-1.js
function hello() {
var localMessage = 'Hello!';
}
var globalMessage = 'Hey there!';
// file-2.js
console.log(localMessage); // localMessage is not defined
console.log(globalMessage); // Hey there!
Có một loại phạm vi khác không được thảo luận nhiều trong bài viết này. Nếu bạn tạo một biến trong một mô-đun JavaScript nhưng bên ngoài một hàm hoặc khối, thì biến đó không có phạm vi toàn cầu mà có phạm vi mô-đun. Các biến có phạm vi mô-đun có sẵn ở bất cứ đâu trong mô-đun hiện tại, nhưng không có trong các tệp hoặc mô-đun khác. Để cho phép các tệp khác truy cập vào một biến ở phạm vi mô-đun, bạn phải xuất biến đó từ mô-đun mà biến được tạo, sau đó import biến đó từ mô-đun cần truy cập vào biến.
Phạm vi cục bộ và phạm vi hàm
Khi bạn tạo các biến trong hàm JavaScript có từ khoá var
, let
hoặc const
, các biến này sẽ nằm trong hàm này nên bạn chỉ có thể truy cập vào chúng từ bên trong hàm. Các biến cục bộ được tạo khi hàm bắt đầu và sẽ bị xoá ngay khi quá trình thực thi hàm kết thúc.
Ví dụ này khai báo biến total
trong hàm addNumbers()
. Bạn chỉ có thể truy cập vào các biến a
, b,
và total
trong hàm addNumbers()
.
function addNumbers(a, b) {
const total = a + b;
}
addNumbers(3, 4);
Bạn có thể sử dụng các từ khoá let
và const
để đặt tên cho biến. Khi bạn sử dụng từ khoá let
, JavaScript có thể cập nhật biến này. Tuy nhiên, với từ khoá const
, biến này vẫn không đổi.
var variable1 = 'Declared with var';
var variable1 = 'Redeclared with var';
variable1; // Redeclared with var
let variable2 = 'Declared with let. Cannot be redeclared.';
variable2 = 'let cannot be redeclared, but can be updated';
variable2; // let cannot be redeclared, but can be updated
const variable3 = 'Declared with const. Cannot be redeclared or updated';
variable3; // Declared with const. Cannot be redeclared or updated
Phạm vi chặn
Các quy tắc chặn được dùng để nhóm một câu lệnh hoặc một tập hợp câu lệnh với nhau. Bạn có thể dùng từ khoá const
hoặc let
để khai báo biến cục bộ trong phạm vi khối. Xin lưu ý rằng bạn không thể dùng từ khoá var
để khai báo các biến có phạm vi khối.
Ví dụ: trong khối này, phạm vi của biến name
và giá trị "Elizabeth"
của biến đó được đặt trong dấu ngoặc nhọn. Các biến trong một phạm vi khối không được sử dụng bên ngoài khối.
{
const name = "Elizabeth";
}
Bạn có thể dùng các biến theo phạm vi khối trong các câu lệnh if
, for
hoặc while
.
Hãy lưu ý hai vòng lặp for
trong đoạn mã này. Một vòng lặp for
dùng từ khoá var
để khai báo biến khởi tạo, biến này tăng lên qua các số 0
, 1
và 2
. Vòng lặp for
còn lại sử dụng từ khoá let
để khai báo biến trình khởi tạo.
for (var i = 0; i < 2; i++) {
// ...
}
console.log(i); // 2
for (let j = 0; j < 2; j++) {
// ...
}
console.log(j); // The j variable isn't defined.
Trong ví dụ về mã trước đó, bạn có thể nhận thấy biến i
trong vòng lặp for
đầu tiên bị rò rỉ bên ngoài vòng lặp for
và vẫn giữ lại giá trị 2
vì từ khoá var
không sử dụng phạm vi khối. Vấn đề này được khắc phục trong vòng lặp for
thứ hai, trong đó biến j
được khai báo bằng từ khoá let
nằm trong phạm vi khối của vòng lặp for
và không tồn tại sau khi vòng lặp for
kết thúc.
Sử dụng lại tên biến trong một phạm vi khác
Phạm vi có thể tách riêng một biến trong hàm, ngay cả khi bạn sử dụng lại cùng một tên biến ở nơi khác trong một phạm vi khác.
Ví dụ này cho bạn thấy cách sử dụng phạm vi cho phép bạn sử dụng lại cùng một tên biến trong nhiều hàm:
function listOne() {
let listItems = 10;
console.log(listItems); // 10
}
function listTwo() {
let listItems = 20;
console.log(listItems); // 20
}
listOne();
listTwo();
Các biến listItems
trong hàm listOne()
và listTwo()
được gán giá trị dự kiến và do đó, không xung đột với nhau.
Phạm vi từ vựng và từ khoá kết thúc
Đóng đề cập đến một hàm đóng, trong đó hàm bên trong có thể truy cập vào phạm vi hàm bên ngoài, còn được gọi là môi trường từ vựng. Do đó, trong JavaScript, bạn sử dụng hàm đóng để cho phép các hàm tham chiếu đến môi trường từ vựng bên ngoài, cho phép mã bên trong một hàm tham chiếu các biến được khai báo bên ngoài hàm. Trên thực tế, bạn có thể mã hoá một chuỗi tham chiếu đến các môi trường từ vựng bên ngoài để một hàm được gọi bởi một hàm, sau đó hàm này được gọi bởi một hàm khác.
Trong ví dụ này, mã tạo thành một đóng bằng môi trường từ vựng được tạo khi hàm outer()
được gọi, đóng qua biến hello
. Do đó, biến hello
được dùng trong hàm callback setTimeout
.
function outer() {
const hello = 'world';
setTimeout(function () {
console.log('Within the closure!', hello)
}, 100);
}
outer();
Với phạm vi từ vựng, phạm vi được xác định trong quá trình biên dịch mã nguồn chứ không phải trong thời gian chạy. Để tìm hiểu thêm về môi trường từ vựng, xem phần Phạm vi và đóng phạm vi.
Mô-đun
Các mô-đun JavaScript giúp sắp xếp mã JavaScript. Khi được sử dụng đúng cách, các phần tử này sẽ tạo nên cấu trúc hiệu quả cho cơ sở mã của bạn và giúp tái sử dụng mã. Thay vì sử dụng các biến toàn cục để chia sẻ biến trên nhiều tệp, mô-đun JavaScript cung cấp kỹ thuật để xuất và import các biến.
// hello.js file
function hello() {
return 'Hello world!';
}
export { hello };
// app.js file
import { hello } from './hello.js';
console.log(hello()); // Hello world!
Bản minh hoạ trình hiển thị phạm vi
Phạm vi là một khái niệm cơ bản mà mọi nhà phát triển JavaScript đều nên nắm được. Để hiểu rõ hơn về hệ thống phạm vi, bạn có thể thử tự viết mã bằng Công cụ hiển thị phạm vi JS. Bản minh hoạ sử dụng màu sắc trong mã để giúp bạn trực quan hoá các phạm vi JavaScript.
Kết luận
Bài viết này giới thiệu nhiều loại phạm vi. Phạm vi JavaScript là một trong những khái niệm nâng cao hơn trong lĩnh vực phát triển web, vì vậy, thật tuyệt khi bạn đã đọc qua nội dung này và dành thời gian để hiểu chủ đề này.
Phạm vi không phải là tính năng dành cho người dùng. Mặc dù vấn đề này chỉ ảnh hưởng đến nhà phát triển web viết mã, nhưng nếu biết cách hoạt động của phạm vi thì bạn có thể sửa lỗi khi chúng phát sinh.