Bài 4.1: Cú pháp và biến trong JavaScript

Chào mừng trở lại với chuỗi bài về Lập trình Web Front-end! Sau khi làm quen với HTML và CSS để "xây nhà" và "trang trí", giờ là lúc chúng ta thổi sự sống vào website của mình với JavaScript. JavaScript là ngôn ngữ kịch bản (scripting language) mạnh mẽ, chạy trên trình duyệt, cho phép chúng ta tạo ra các trang web tương tác, linh hoạtđầy đủ chức năng.

Để bắt đầu cuộc hành trình với JavaScript, hai khái niệm đầu tiên và quan trọng nhất bạn cần nắm vững là cú pháp (syntax) và biến (variables). Chúng là những viên gạch cơ bản để xây dựng mọi chương trình JavaScript.

Cú pháp (Syntax) là gì?

Hãy tưởng tượng JavaScript là một ngôn ngữ mà trình duyệt web của bạn cần hiểu. Cũng như tiếng Việt hay tiếng Anh có ngữ pháp, JavaScript cũng có các quy tắc riêng về cách viết các câu lệnh (statements) để máy tính có thể đọc và thực thi. Đó chính là cú pháp. Viết sai cú pháp giống như nói sai ngữ pháp vậy, máy tính sẽ không hiểu bạn muốn làm gì và sẽ báo lỗi!

Các thành phần cơ bản của cú pháp JS:
1. Câu lệnh (Statements)

Mỗi hành động mà bạn muốn JavaScript thực hiện được viết thành một câu lệnh. Một chương trình JavaScript là một tập hợp các câu lệnh được thực thi theo thứ tự.

console.log("Xin chào, thế giới!"); // Câu lệnh in dòng chữ ra console
let soTuoi = 30; // Câu lệnh khai báo và gán giá trị cho biến
alert("Đây là một cảnh báo!"); // Câu lệnh hiển thị hộp thoại cảnh báo

Giải thích: Mỗi dòng code trên là một câu lệnh riêng biệt. Dòng đầu tiên gọi một hàm tích hợp (console.log) để hiển thị thông báo. Dòng thứ hai sử dụng từ khóa let để tạo ra một biến mới và gán giá trị cho nó. Dòng thứ ba sử dụng hàm alert để tạo ra một popup trên trình duyệt.

2. Dấu chấm phẩy (Semicolons - ;)

Theo truyền thống, mỗi câu lệnh trong JavaScript thường kết thúc bằng dấu chấm phẩy (;). Điều này giúp JavaScript biết đâu là điểm kết thúc của một câu lệnh, đặc biệt khi bạn viết nhiều câu lệnh trên cùng một dòng (mặc dù không được khuyến khích vì khó đọc).

let ten = "FullhouseDev"; // Kết thúc bằng ;
let ngheNghiep = "Developer"; // Kết thúc bằng ;

// Viết nhiều câu lệnh trên cùng dòng (hợp lệ, nhưng khó đọc):
let a = 1; let b = 2; let c = a + b;

Giải thích: Dấu chấm phẩy phân tách các câu lệnh. Lưu ý: Trong JavaScript hiện đại (từ ES6 trở đi), dấu chấm phẩy thường có thể bỏ qua nếu câu lệnh nằm trên một dòng riêng. JavaScript có một cơ chế gọi là Automatic Semicolon Insertion (ASI) tự động thêm dấu chấm phẩy vào cuối dòng trong nhiều trường hợp. Tuy nhiên, thêm dấu chấm phẩy một cách nhất quán vẫn là thực hành tốt (good practice) để tránh các lỗi tiềm ẩn và làm cho code của bạn rõ ràng hơn, đặc biệt trong các tình huống phức tạp hoặc khi sử dụng các công cụ nén mã.

// Ví dụ ASI tự động thêm dấu chấm phẩy
let x = 10
let y = 20 // ASI sẽ thêm ; vào cuối dòng này

// Tốt hơn nên viết tường minh:
let x = 10;
let y = 20;

Giải thích: Trong ví dụ đầu tiên, JavaScript tự động thêm dấu chấm phẩy vào cuối dòng let x = 10. Tuy nhiên, dựa vào ASI có thể gây ra các lỗi khó hiểu trong một số trường hợp nhất định. Do đó, hãy cứ thêm dấu chấm phẩy vào cuối mỗi câu lệnh.

3. Bình luận (Comments)

Bình luận là những đoạn văn bản trong mã mà JavaScript sẽ hoàn toàn bỏ qua khi thực thi. Chúng cực kỳ hữu ích để:

  • Giải thích mã của bạn hoạt động như thế nào cho chính bạn trong tương lai hoặc cho người khác đọc code của bạn.
  • Ghi chú các điểm cần cải thiện hoặc lý do của một quyết định thiết kế.
  • Tạm thời vô hiệu hóa một đoạn mã để kiểm tra hoặc gỡ lỗi mà không cần xóa nó.

Có hai loại bình luận:

  • Bình luận một dòng: Bắt đầu bằng //. Mọi thứ từ // đến cuối dòng đều là bình luận.
    // Đây là một bình luận một dòng giải thích mục đích của dòng code dưới
    let diemSo = 95; // Gán điểm số cho sinh viên A
    
  • Bình luận nhiều dòng: Bắt đầu bằng /* và kết thúc bằng */. Mọi thứ nằm giữa hai cặp ký tự này đều là bình luận, bất kể có bao nhiêu dòng.
    /*
    Đây là một khối bình luận
    bao gồm nhiều dòng.
    Thường dùng để giải thích chi tiết
    hoặc mô tả một chức năng lớn.
    */
    let thongTin = "Đang xử lý dữ liệu...";
    
    Giải thích: Bình luận không ảnh hưởng đến cách chương trình chạy, chúng chỉ dành cho con người đọc mã. Sử dụng bình luận một cách hiệu quả là một kỹ năng quan trọng của lập trình viên giỏi.
4. Tính nhạy cảm với chữ hoa/thường (Case Sensitivity)

JavaScript là ngôn ngữ nhạy cảm với chữ hoa/thường. Điều này có nghĩa là myVariable, myvariable, và MyVariable được coi là ba tên khác nhau hoàn toàn bởi JavaScript. Từ khóa (let, const, function, v.v.) cũng phải được viết đúng chữ thường.

let tenNguoiDung = "Alice";
// console.log(TenNguoiDung); // Lỗi! JavaScript không tìm thấy biến TenNguoiDung (chữ T hoa)
console.log(tenNguoiDung); // Đúng! Sử dụng đúng tên biến (chữ t thường)

Giải thích: Việc viết sai chữ hoa/thường là một lỗi phổ biến khi mới bắt đầu học JS. Luôn chú ý đến cách bạn viết hoa/thường tên biến, tên hàm, và các từ khóa.

5. Khoảng trắng (Whitespace)

JavaScript thường bỏ qua các khoảng trắng thừa (dấu cách `, tab\t, xuống dòng\n`) giữa các từ khóa, tên biến, toán tử, dấu ngoặc, v.v. Điều này cho phép bạn định dạng mã của mình theo cách dễ đọc nhất.

let     tuoi    =    25   ; // Hợp lệ, nhưng khó đọc
let ten = "Alice"; console.log(ten); // Hợp lệ (hai câu lệnh trên một dòng)

// Nên viết thế này để dễ đọc:
let giaTri = 100;
console.log(giaTri);

// Hoặc thế này cho các biểu thức dài:
let tinNhanChaoMung =
  "Chào mừng bạn đến với trang web của chúng tôi!";

Giải thích: JavaScript không quan tâm đến số lượng khoảng trắng hay dòng trống giữa các phần tử của cú pháp (miễn là không phá vỡ cấu trúc cơ bản). Điều này mang lại cho bạn sự linh hoạt trong việc định dạng code. Hãy tận dụng nó để làm cho code của bạn sạch sẽ và dễ bảo trì.

// Khoảng trắng xung quanh toán tử giúp dễ đọc:
let ketQuaPhepTinh = 10 + 5 * 2 - (8 / 4); // Dễ đọc hơn nhiều so với 10+5*2-(8/4)
console.log(ketQuaPhepTinh); // Output: 18

Giải thích: Việc thêm khoảng trắng xung quanh các toán tử như +, -, *, / giúp mắt dễ dàng phân tích biểu thức.

// Khoảng trắng trong các cấu trúc điều khiển (sẽ học sau)
if (diem > 7) {
  // code
}

for (let i = 0; i < 10; i++) {
  // code
}

function tenHam(thamSo) {
  // code
}

Giải thích: Khoảng trắng trong các cấu trúc như if, for, function giúp phân tách rõ ràng các phần của cấu trúc và khối mã bên trong.

Tóm lại về cú pháp: Nắm vững những quy tắc cơ bản này là thiết yếu. Chúng là nền tảng để bạn viết mã JavaScript mà máy tính có thể hiểu và thực thi đúng. Hãy luyện tập viết code sạch sẽ, dễ đọc ngay từ đầu!

Biến (Variables) - "Thùng chứa" dữ liệu của bạn

Trong lập trình, chúng ta thường cần lưu trữ các mẩu thông tin tạm thời hoặc lâu dài để sử dụng sau này trong chương trình. Ví dụ: tên người dùng, số điểm, trạng thái bật/tắt, kết quả tính toán, v.v. Đó là lúc biến xuất hiện! Hãy nghĩ về biến như những chiếc thùng (containers) có tên, nơi bạn có thể đặt dữ liệu vào đó, lấy dữ liệu ra hoặc thay thế dữ liệu bên trong bất cứ khi nào cần.

Khai báo biến

Trước khi sử dụng một biến, bạn cần phải "khai báo" nó. Tức là, bạn nói cho JavaScript biết rằng bạn sẽ cần một "thùng chứa" dữ liệu và đặt cho nó một cái tên. Trong JavaScript hiện đại, chúng ta thường dùng hai từ khóa chính để khai báo biến: letconst.

let - Biến có thể thay đổi giá trị

Từ khóa let dùng để khai báo một biến mà bạn dự định sẽ thay đổi giá trị của nó trong quá trình chạy chương trình. Đây là cách phổ biến nhất để khai báo biến thông thường.

let soLuongSanPham = 10; // Khai báo biến soLuongSanPham và gán giá trị ban đầu là 10
console.log("Số lượng ban đầu:", soLuongSanPham); // Output: Số lượng ban đầu: 10

soLuongSanPham = 15; // Thay đổi giá trị của biến
console.log("Số lượng sau khi cập nhật:", soLuongSanPham); // Output: Số lượng sau khi cập nhật: 15

let tenNguoiDung; // Khai báo biến nhưng chưa gán giá trị
console.log("Tên người dùng (chưa gán):", tenNguoiDung); // Output: Tên người dùng (chưa gán): undefined (chưa có giá trị)

tenNguoiDung = "Alice"; // Gán giá trị sau khi khai báo
console.log("Tên người dùng (đã gán):", tenNguoiDung); // Output: Tên người dùng (đã gán): Alice

Giải thích: Chúng ta sử dụng let để tạo các biến soLuongSanPhamtenNguoiDung. Biến soLuongSanPham được gán lại giá trị mới là 15. Biến tenNguoiDung ban đầu không được gán giá trị, nên giá trị mặc định của nó là undefined. Sau đó, chúng ta gán giá trị "Alice" cho nó. Biến khai báo bằng let có thể được gán giá trị lúc khai báo hoặc sau khi khai báo, và giá trị của nó có thể thay đổi nhiều lần.

const - Biến không thể thay đổi (Hằng số)

Từ khóa const dùng để khai báo một hằng số (constant). Điều này có nghĩa là sau khi bạn gán giá trị ban đầu cho nó, bạn không thể gán lại giá trị khác cho biến đó nữa trong suốt quá trình chạy chương trình. const rất hữu ích cho các giá trị mà bạn biết sẽ không bao giờ thay đổi (ví dụ: số Pi, tên ứng dụng, URL cố định).

const PI = 3.14159; // Khai báo hằng số PI
console.log("Giá trị của PI:", PI); // Output: Giá trị của PI: 3.14159

// PI = 3.0; // Lỗi! Bạn không thể gán lại giá trị cho hằng số đã khai báo với const
// console.log(PI); // Dòng này sẽ không chạy được nếu dòng trên gây lỗi

const TEN_APP = "Quản lý công việc"; // Khai báo hằng số chuỗi
console.log("Tên ứng dụng:", TEN_APP); // Output: Tên ứng dụng: Quản lý công việc

// const soNgayTrongTuan; // Lỗi! Phải gán giá trị ngay khi khai báo const
const soNgayTrongTuan = 7; // Đúng!
console.log("Số ngày trong tuần:", soNgayTrongTuan); // Output: Số ngày trong tuần: 7

Giải thích: Biến PITEN_APP được khai báo bằng const và được gán giá trị ngay lập tức. Dòng code cố gắng gán lại giá trị cho PI sẽ gây ra lỗi TypeError (Assignment to constant variable). Một yêu cầu bắt buộc khi dùng const là bạn phải gán giá trị cho nó ngay tại thời điểm khai báo. Sử dụng const giúp code của bạn dễ dự đoán hơn, bởi vì bạn biết chắc giá trị của biến đó sẽ không thay đổi.

Nguyên tắc chung: Hãy ưu tiên dùng const. Nếu bạn chắc chắn rằng biến sẽ không thay đổi giá trị sau khi được gán lần đầu, hãy dùng const. Điều này không chỉ thể hiện rõ ràng ý định của bạn trong code mà còn giúp JavaScript (và các công cụ khác) tối ưu hóa hiệu suất. Chỉ dùng let khi bạn biết rằng giá trị của biến sẽ cần được cập nhật sau này.

var - Cách cũ (Ít dùng trong mã hiện đại)

Trước khi letconst ra đời (từ phiên bản ES6 - ECMAScript 2015), var là từ khóa duy nhất để khai báo biến. var có một số hành vi đặc trưng về phạm vi (scope) và hoisting có thể gây nhầm lẫn và lỗi không mong muốn, đặc biệt đối với người mới bắt đầu hoặc trong các dự án lớn. Do đó, trong mã JavaScript hiện đại, letconst được khuyến khích sử dụng thay cho var.

var tenCuaToi = "Bob"; // Khai báo bằng var
console.log("Tên ban đầu (var):", tenCuaToi); // Output: Tên ban đầu (var): Bob

tenCuaToi = "Charlie"; // Vẫn có thể thay đổi giá trị
console.log("Tên sau khi thay đổi (var):", tenCuaToi); // Output: Tên sau khi thay đổi (var): Charlie

// var có thể khai báo lại trong cùng phạm vi (let và const thì không!)
var tenCuaToi = "David"; // Không báo lỗi! Dễ dàng ghi đè biến mà không biết
console.log("Tên sau khi khai báo lại (var):", tenCuaToi); // Output: Tên sau khi khai báo lại (var): David

Giải thích: Biến khai báo bằng var cũng có thể thay đổi giá trị. Tuy nhiên, khả năng khai báo lại một biến với cùng tên trong cùng phạm vi mà không gặp lỗi là một điểm yếu lớn của var, có thể dẫn đến bug khó lường. letconst giải quyết vấn đề này bằng cách báo lỗi nếu bạn cố gắng khai báo lại biến trong cùng phạm vi.

Lời khuyên: Với các dự án mới và khi học JavaScript hiện đại, hãy tập trung vào letconst. Bạn sẽ ít gặp phải các lỗi liên quan đến phạm vi và hoisting của var. Bạn có thể sẽ gặp var trong các đoạn mã cũ (legacy code), nhưng tốt nhất là hiểu lý do tại sao nó ít được dùng trong code mới.

Quy tắc đặt tên biến

Việc đặt tên biến rất quan trọng để mã của bạn dễ đọcdễ hiểu. Tên biến phải mô tả được mục đích của dữ liệu nó lưu trữ. Có một số quy tắc bắt buộc và quy ước phổ biến cần tuân thủ:

  1. Quy tắc bắt buộc (Syntax Rules):

    • Tên biến chỉ có thể chứa các chữ cái (a-z, A-Z), chữ số (0-9), dấu gạch dưới (_) và dấu đô la ($).
    • Tên biến không được bắt đầu bằng chữ số.
    • Tên biến không được trùng với các từ khóa dành riêng (reserved keywords) của JavaScript (như let, const, function, if, for, while, class, return, v.v.).
    • JavaScript phân biệt chữ hoa/thường (myVar khác myvar).
  2. Quy ước phổ biến (Coding Conventions):

    • Sử dụng camelCase: Đây là quy ước phổ biến nhất trong cộng đồng JavaScript. Tên biến bắt đầu bằng chữ thường, các từ tiếp theo viết hoa chữ cái đầu (ví dụ: firstName, soLuongSanPham, tongGiaTriDonHang).
    • Sử dụng tên biến có ý nghĩa: Tên biến nên mô tả rõ ràng dữ liệu mà nó lưu trữ (ví dụ: soSinhVien thay vì s, tenDayDu thay vì t).
    • Đối với hằng số (const) mà giá trị không thay đổi và được coi là "toàn cục" hoặc rất quan trọng, đôi khi người ta dùng chữ hoa với dấu gạch dưới (SNAKE_CASE) (ví dụ: MAX_ATTEMPTS, API_KEY, DEFAULT_LANGUAGE). Tuy nhiên, dùng camelCase với const cũng hoàn toàn phổ biến và được chấp nhận. Điều quan trọng là sự nhất quán trong dự án của bạn.
// Tên biến hợp lệ theo quy tắc:
let firstName = "Peter"; // Chữ cái
let $price = 50; // Bắt đầu bằng $
let _counter = 0; // Bắt đầu bằng _
let user1 = "John"; // Chứa số (không bắt đầu bằng số)
let tenDayDuCuaToi = "Nguyen Van A"; // Chứa chữ, chữ hoa, camelCase

// Tên biến KHÔNG hợp lệ (sẽ gây lỗi SyntaxError):
// let 1user = "Mary"; // Lỗi: Bắt đầu bằng số
// let my-variable = 10; // Lỗi: Chứa dấu gạch ngang (-)
// let const = "something"; // Lỗi: Trùng với từ khóa dành riêng
// let function = "myFunc"; // Lỗi: Trùng với từ khóa dành riêng

Giải thích: Các ví dụ trên minh họa quy tắc đặt tên. Hãy luôn chọn tên biến sao cho người đọc code (bao gồm cả bạn trong tương lai!) có thể hiểu ngay biến đó dùng để làm gì.

Gán giá trị cho biến

Bạn sử dụng toán tử gán (=) để đặt giá trị vào một biến sau khi nó đã được khai báo (hoặc gán ngay lúc khai báo).

let diemToan; // Khai báo biến diemToan (giá trị hiện tại là undefined)

diemToan = 9; // Gán giá trị 9 vào biến diemToan
console.log("Điểm Toán:", diemToan); // Output: Điểm Toán: 9

let diemVan = 8; // Khai báo và gán giá trị 8 ngay lúc tạo biến
console.log("Điểm Văn:", diemVan); // Output: Điểm Văn: 8

let tongDiem = diemToan + diemVan; // Gán kết quả của một phép tính vào biến tongDiem
console.log("Tổng điểm:", tongDiem); // Output: Tổng điểm: 17

Giải thích: Dòng đầu tiên chỉ khai báo biến diemToan. Dòng thứ hai dùng = để gán giá trị. Dòng thứ ba là cách phổ biến để khai báo và gán cùng lúc. Dòng cuối cùng cho thấy bạn có thể gán kết quả của một biểu thức (trong trường hợp này là phép cộng) vào một biến.

Bạn có thể gán giá trị của biến này cho biến khác:

let soLuongTonKho = 50;
let soLuongCanXuat = soLuongTonKho; // Gán giá trị hiện tại của soLuongTonKho cho soLuongCanXuat
console.log("Số lượng cần xuất:", soLuongCanXuat); // Output: Số lượng cần xuất: 50

// Nếu soLuongTonKho thay đổi sau đó, soLuongCanXuat KHÔNG tự động thay đổi:
soLuongTonKho = 30;
console.log("Số lượng tồn kho mới:", soLuongTonKho); // Output: Số lượng tồn kho mới: 30
console.log("Số lượng cần xuất (vẫn giữ giá trị cũ):", soLuongCanXuat); // Output: Số lượng cần xuất (vẫn giữ giá trị cũ): 50

Giải thích: Khi bạn gán giá trị từ biến này sang biến khác (ví dụ soLuongCanXuat = soLuongTonKho), bạn đang sao chép giá trị tại thời điểm đó. Hai biến sau đó hoàn toàn độc lập. Thay đổi giá trị của biến gốc (soLuongTonKho) sẽ không ảnh hưởng đến biến đã nhận giá trị sao chép (soLuongCanXuat).

Các kiểu dữ liệu cơ bản (Giới thiệu sơ lược)

Biến trong JavaScript có thể chứa nhiều loại dữ liệu khác nhau. Điều thú vị là JavaScript là ngôn ngữ linh hoạt (dynamically typed), nghĩa là bạn không cần khai báo kiểu dữ liệu khi khai báo biến (khác với các ngôn ngữ như Java hay C#). Kiểu dữ liệu của biến được xác định bởi giá trị mà nó đang giữ.

Dưới đây là một số kiểu dữ liệu cơ bản nhất mà bạn sẽ làm việc thường xuyên (chúng ta sẽ có bài chi tiết hơn về các kiểu dữ liệu và sự khác biệt giữa kiểu nguyên thủy và đối tượng sau):

  • Number: Biểu diễn số, bao gồm cả số nguyên (integer) và số thập phân (floating-point) (ví dụ: 10, 3.14, -5, 0.001).
  • String: Biểu diễn chuỗi ký tự (văn bản), được đặt trong dấu nháy đơn ('), nháy kép (") hoặc backticks (`) (ví dụ: "Hello World", 'Chào bạn', `Tên tôi là ${ten}` - backticks cho phép nhúng biến trực tiếp).
  • Boolean: Chỉ có hai giá trị: true (đúng) hoặc false (sai). Thường dùng trong các điều kiện logic (ví dụ: isLoggedIn = true, isVisible = false).
  • Undefined: Giá trị mặc định của một biến đã được khai báo nhưng chưa được gán bất kỳ giá trị nào.
  • Null: Biểu thị một giá trị rỗng hoặc không tồn tại một cách có chủ đích. Khác với undefined, null là một giá trị được gán vào biến một cách rõ ràng.
  • Symbol: Kiểu dữ liệu mới trong ES6, tạo ra các giá trị duy nhất không trùng lặp.
  • BigInt: Kiểu dữ liệu mới, biểu diễn số nguyên lớn hơn phạm vi an toàn của Number.
let soLuong = 150; // Kiểu Number
let tenHang = "Áo thun"; // Kiểu String (dùng nháy kép)
let daHoanThanh = false; // Kiểu Boolean
let ngayGiaoHang; // Kiểu Undefined (chưa gán giá trị)
let maGiamGia = null; // Kiểu Null (chủ đích là không có mã)

console.log("Số lượng:", soLuong); // Output: Số lượng: 150
console.log("Tên hàng:", tenHang); // Output: Tên hàng: Áo thun
console.log("Đã hoàn thành?", daHoanThanh); // Output: Đã hoàn thành? false
console.log("Ngày giao hàng:", ngayGiaoHang); // Output: Ngày giao hàng: undefined
console.log("Mã giảm giá:", maGiamGia); // Output: Mã giảm giá: null

Giải thích: Các ví dụ này cho thấy biến có thể lưu trữ các loại dữ liệu cơ bản khác nhau. Kiểu dữ liệu của biến ngayGiaoHangundefined vì nó chỉ được khai báo mà chưa được gán giá trị ban đầu. Biến maGiamGia được gán rõ ràng giá trị null để chỉ ra rằng hiện tại không có mã giảm giá nào.

// Ví dụ về tính linh hoạt của kiểu dữ liệu trong biến (dynamically typed)
let giaTriBatKy = 100; // Ban đầu, giaTriBatKy là Number
console.log("Giá trị ban đầu:", giaTriBatKy, typeof giaTriBatKy); // typeof giúp kiểm tra kiểu dữ liệu - Output: Giá trị ban đầu: 100 number

giaTriBatKy = "Xin chào"; // Bây giờ, giaTriBatKy là String
console.log("Giá trị mới:", giaTriBatKy, typeof giaTriBatKy); // Output: Giá trị mới: Xin chào string

giaTriBatKy = true; // Bây giờ, giaTriBatKy là Boolean
console.log("Giá trị mới nữa:", giaTriBatKy, typeof giaTriBatKy); // Output: Giá trị mới nữa: true boolean

Giải thích: Bạn có thể gán các kiểu dữ liệu khác nhau cho cùng một biến (được khai báo bằng let hoặc var) trong suốt quá trình chạy chương trình. JavaScript sẽ tự động điều chỉnh kiểu dữ liệu của biến theo giá trị mới được gán. Tính năng này rất linh hoạt nhưng đôi khi cũng có thể gây ra lỗi nếu bạn không cẩn thận kiểm soát kiểu dữ liệu của biến trong các phần khác nhau của code.

Biến và Phạm vi (Scope - Giới thiệu sơ lược)

Một khái niệm quan trọng liên quan đến biến là phạm vi (scope). Phạm vi xác định nơi nào trong code bạn có thể truy cập được một biến.

  • Biến khai báo với var có phạm vi là hàm (function scope) hoặc toàn cục (global scope).
  • Biến khai báo với letconst có phạm vi là khối (block scope). Một "khối" là bất kỳ đoạn code nào nằm trong dấu ngoặc nhọn {} (ví dụ: trong if, for, while, hoặc đơn giản là một khối độc lập).

Sự khác biệt về phạm vi này là lý do chính khiến letconst được ưu tiên hơn var trong code hiện đại, vì block scope giúp tránh được nhiều lỗi không mong muốn liên quan đến việc truy cập biến ở những nơi không ngờ tới.

// Ví dụ về Block Scope (let và const)
if (true) {
  let tenDiaPhuong = "Hà Nội"; // Biến này chỉ tồn tại bên trong khối if
  const MA_VUNG = "84"; // Hằng số này cũng chỉ tồn tại bên trong khối if
  console.log("Trong khối if:", tenDiaPhuong, MA_VUNG); // Output: Trong khối if: Hà Nội 84
}

// console.log("Ngoài khối if:", tenDiaPhuong); // Lỗi! ReferenceError: tenDiaPhuong is not defined
// console.log("Ngoài khối if:", MA_VUNG); // Lỗi! ReferenceError: MA_VUNG is not defined

// Ví dụ về Function Scope (var)
function chaoToi() {
  var tinNhanChao = "Xin chào từ hàm!"; // Biến này chỉ tồn tại bên trong hàm chaoToi
  console.log(tinNhanChao); // Output: Xin chào từ hàm!
}

chaoToi();
// console.log(tinNhanChao); // Lỗi! ReferenceError: tinNhanChao is not defined

// Biến toàn cục (khai báo ngoài mọi hàm/khối hoặc dùng var ngoài strict mode)
let bienToanCuc = "Tôi là biến toàn cục";
var bienToanCucVar = "Tôi cũng là biến toàn cục"; // Tránh dùng cách này

console.log(bienToanCuc); // Output: Tôi là biến toàn cục
console.log(bienToanCucVar); // Output: Tôi cũng là biến toàn cục

Giải thích: Ví dụ minh họa rằng các biến tenDiaPhuongMA_VUNG (khai báo bằng letconst) chỉ có thể truy cập được bên trong cặp ngoặc nhọn {} của câu lệnh if. Tương tự, biến tinNhanChao (khai báo bằng var bên trong hàm) chỉ có thể truy cập được bên trong hàm chaoToi(). Cố gắng truy cập chúng bên ngoài phạm vi của chúng sẽ gây ra lỗi ReferenceError. Biến toàn cục có thể truy cập từ bất cứ đâu trong chương trình. Chúng ta sẽ tìm hiểu sâu hơn về Scope trong một bài riêng.

Tóm lại về biến: Biến là linh hồn của mọi chương trình, giúp bạn lưu trữ và quản lý dữ liệu. Hiểu rõ sự khác biệt giữa letconst (và lý do ít dùng var), nắm vững quy tắc đặt tên, và hiểu cách gán giá trị là những kỹ năng cực kỳ quan trọng khi làm việc với JavaScript.

Kết hợp Cú pháp và Biến trong thực tế

Trong thực tế, cú pháp và biến luôn đi đôi với nhau. Bạn sử dụng cú pháp để viết các câu lệnh khai báo biến, gán giá trị, thực hiện các phép toán, và hiển thị kết quả của biến.

// Một đoạn code kết hợp cú pháp và biến:

/*
Đây là một chương trình nhỏ
để tính diện tích hình chữ nhật
và hiển thị kết quả.
*/

// Khai báo các biến sử dụng 'let' vì giá trị có thể thay đổi
let chieuDai = 15; // Chiều dài hình chữ nhật (Number)
let chieuRong = 10; // Chiều rộng hình chữ nhật (Number)

// Khai báo biến để lưu kết quả, sử dụng 'let'
let dienTich; // Ban đầu undefined

// Sử dụng cú pháp toán tử để tính diện tích và gán vào biến dienTich
dienTich = chieuDai * chieuRong; // Phép nhân và gán giá trị (có khoảng trắng hợp lý)

// Sử dụng console.log (cú pháp gọi hàm) và nối chuỗi (cú pháp toán tử +)
// để hiển thị kết quả ra console
console.log("Chiều dài:", chieuDai, "đơn vị"); // In giá trị biến chieuDai
console.log("Chiều rộng:", chieuRong, "đơn vị"); // In giá trị biến chieuRong
console.log("Diện tích hình chữ nhật là: " + dienTich + " đơn vị vuông."); // In kết quả tính toán
// console.log(`Diện tích hình chữ nhật là: ${dienTich} đơn vị vuông.`); // Cách khác dùng template literals (backticks) - sẽ học sau

// Giả sử bạn muốn tính lại diện tích với kích thước khác:
chieuDai = 20; // Cập nhật giá trị biến (khai báo bằng let)
chieuRong = 12;

dienTich = chieuDai * chieuRong; // Tính toán lại

console.log("\n--- Sau khi thay đổi kích thước ---"); // Dấu \n tạo dòng mới
console.log("Chiều dài mới:", chieuDai, "đơn vị");
console.log("Chiều rộng mới:", chieuRong, "đơn vị");
console.log("Diện tích hình chữ nhật mới là: " + dienTich + " đơn vị vuông.");

// Một hằng số (const)
const DON_VI_DO = "cm";
console.log("Đơn vị đo đang sử dụng:", DON_VI_DO);

// DON_VI_DO = "m"; // Lỗi! Không thể thay đổi hằng số

Giải thích: Đoạn code trên minh họa việc sử dụng cú pháp bình luận, khai báo biến với letconst, gán giá trị bằng =, thực hiện phép tính *, và sử dụng cú pháp gọi hàm console.log cùng với toán tử + để nối chuỗi và hiển thị thông tin. Chúng ta thấy biến let (chieuDai, chieuRong, dienTich) có thể thay đổi giá trị, trong khi hằng số const (DON_VI_DO) thì không. Việc sử dụng tên biến có ý nghĩa (chieuDai, chieuRong, dienTich) và khoảng trắng hợp lý giúp đoạn code dễ đọc và hiểu.

// Ví dụ về sử dụng biến trong các điều kiện (sẽ học kỹ hơn trong bài về Cấu trúc điều khiển)
let diemTrungBinh = 8.5; // Kiểu Number
let daHoanThanhKhoaHoc = true; // Kiểu Boolean

if (diemTrungBinh >= 8.0 && daHoanThanhKhoaHoc) { // Kiểm tra giá trị của biến
    let loaiTotNghiep = "Giỏi"; // Biến mới trong block scope của if
    console.log("Chúc mừng! Bạn tốt nghiệp loại:", loaiTotNghiep);
} else {
    console.log("Cố gắng hơn nhé!");
}

// console.log(loaiTotNghiep); // Lỗi! loaiTotNghiep không tồn tại ở đây

Giải thích: Biến diemTrungBinhdaHoanThanhKhoaHoc được sử dụng trong điều kiện của câu lệnh if. Nếu điều kiện đúng (cả hai biến có giá trị phù hợp), khối code bên trong if sẽ được thực thi. Biến loaiTotNghiep được khai báo bằng let bên trong khối if, do đó nó chỉ tồn tại trong phạm vi đó (block scope).

Comments

There are no comments at the moment.