Biến

Biến là một cấu trúc dữ liệu gán tên đại diện cho một giá trị. Các thuộc tính này có thể chứa dữ liệu thuộc bất kỳ loại nào.

Tên của biến được gọi là giá trị nhận dạng. Giá trị nhận dạng hợp lệ phải tuân theo các quy tắc sau:

  • Giá trị nhận dạng có thể chứa chữ cái Unicode, dấu đô la ($), ký tự dấu gạch dưới (_), chữ số (0-9) và thậm chí một số ký tự Unicode.
  • Giá trị nhận dạng không được chứa khoảng trắng vì trình phân tích cú pháp sử dụng khoảng trắng để phân tách các phần tử đầu vào. Ví dụ: nếu bạn cố gắng gọi một biến my Variable thay vì myVariable, trình phân tích cú pháp sẽ thấy hai giá trị nhận dạng là myVariable, đồng thời gửi một lỗi cú pháp ("mã thông báo không mong muốn: giá trị nhận dạng").
  • Giá trị nhận dạng phải bắt đầu bằng một chữ cái, dấu gạch dưới (_) hoặc dấu đô la ($). Giá trị nhận dạng không được bắt đầu bằng chữ số để tránh nhầm lẫn giữa số và giá trị nhận dạng:

    let 1a = true;
    
    > Uncaught SyntaxError: Invalid or unexpected token
    

    Nếu JavaScript cho phép số ở đầu giá trị nhận dạng, thì điều đó sẽ cho phép giá trị nhận dạng chỉ bao gồm số, gây ra xung đột giữa số dùng làm số và số dùng làm giá trị nhận dạng:

    let 10 = 20
    
    10 + 5
    > ?
    
  • Bạn không thể sử dụng "Từ dành riêng" có ý nghĩa về cú pháp làm giá trị nhận dạng.

  • Giá trị nhận dạng không được chứa ký tự đặc biệt (! . , / \ + - * =).

Sau đây không phải là các quy tắc nghiêm ngặt để tạo giá trị nhận dạng, mà là các phương pháp hay nhất trong ngành giúp bạn duy trì mã dễ dàng hơn. Nếu dự án cụ thể của bạn có các tiêu chuẩn khác, hãy tuân theo các tiêu chuẩn đó để đảm bảo tính nhất quán.

Theo ví dụ do các phương thức và thuộc tính tích hợp của JavaScript đặt ra, camel case (còn được viết tắt là "camelCase") là một quy ước rất phổ biến cho các giá trị nhận dạng bao gồm nhiều từ. Kiểu viết lạc đà là cách viết hoa chữ cái đầu tiên của mỗi từ, ngoại trừ chữ cái đầu tiên để cải thiện khả năng đọc mà không cần dấu cách.

let camelCasedIdentifier = true;

Một số dự án sử dụng các quy ước đặt tên khác tuỳ thuộc vào ngữ cảnh và bản chất của dữ liệu. Ví dụ: chữ cái đầu tiên của một lớp thường được viết hoa, vì vậy, tên lớp gồm nhiều từ thường sử dụng một biến thể của kiểu viết liền với chữ cái đầu viết hoa, thường được gọi là "kiểu viết liền với chữ cái đầu viết hoa" hoặc kiểu viết Pascal.

class MyClass {

}

Giá trị nhận dạng phải mô tả ngắn gọn bản chất của dữ liệu mà chúng chứa (ví dụ: currentMonthDays là tên tốt hơn theNumberOfDaysInTheCurrentMonth) và đọc rõ ràng ngay từ đầu (originalValue tốt hơn val). Giá trị nhận dạng myVariable được sử dụng trong toàn bộ mô-đun này hoạt động trong ngữ cảnh của các ví dụ riêng biệt, nhưng sẽ rất không hữu ích trong mã sản xuất vì chúng không cung cấp thông tin về dữ liệu mà chúng chứa.

Giá trị nhận dạng không được quá cụ thể về dữ liệu mà chúng chứa, vì giá trị của các giá trị nhận dạng này có thể thay đổi tuỳ thuộc vào cách tập lệnh hoạt động trên dữ liệu đó hoặc tuỳ thuộc vào quyết định mà người bảo trì trong tương lai đưa ra. Ví dụ: một biến ban đầu được gán giá trị nhận dạng miles có thể cần được thay đổi thành một giá trị tính bằng km trong dự án sau này, yêu cầu người bảo trì thay đổi mọi tệp tham chiếu đến biến đó để tránh nhầm lẫn trong tương lai. Để tránh điều này, hãy sử dụng distance làm giá trị nhận dạng.

JavaScript không cấp bất kỳ đặc quyền hoặc ý nghĩa đặc biệt nào cho các giá trị nhận dạng bắt đầu bằng ký tự dấu gạch dưới (_), nhưng các giá trị nhận dạng này thường được dùng để cho biết một biến, phương thức hoặc thuộc tính là "riêng tư", nghĩa là chỉ dùng được trong ngữ cảnh của đối tượng chứa biến, phương thức hoặc thuộc tính đó và không được truy cập hoặc sửa đổi bên ngoài ngữ cảnh đó. Đây là quy ước được chuyển từ các ngôn ngữ lập trình khác và có trước khi thêm thuộc tính riêng tư của JavaScript.

Khai báo biến

Có nhiều cách để cho JavaScript biết về một giá trị nhận dạng, một quy trình được gọi là "khai báo" biến. Biến được khai báo bằng từ khoá let, const hoặc var.

let myVariable;

Sử dụng let hoặc var để khai báo một biến có thể thay đổi bất cứ lúc nào. Các từ khoá này cho trình thông dịch JavaScript biết rằng một chuỗi ký tự là một giá trị nhận dạng có thể chứa một giá trị.

Khi làm việc trong cơ sở mã hiện đại, hãy sử dụng let thay vì var. var vẫn hoạt động trong các trình duyệt hiện đại, nhưng có một số hành vi không trực quan được xác định trong các phiên bản JavaScript đầu tiên và sau đó không thể thay đổi để duy trì khả năng tương thích ngược. let được thêm vào ES6 để giải quyết một số vấn đề về thiết kế của var.

Biến đã khai báo được khởi tạo bằng cách gán giá trị cho biến đó. Sử dụng một dấu bằng (=) để gán hoặc chỉ định lại giá trị cho một biến. Bạn có thể thực hiện việc này trong cùng một câu lệnh khai báo:

let myVariable = 5;

myVariable + myVariable
> 10

Bạn cũng có thể khai báo biến bằng let (hoặc var) mà không cần khởi chạy biến ngay lập tức. Nếu bạn làm như vậy, giá trị ban đầu của biến sẽ là undefined cho đến khi mã của bạn chỉ định giá trị cho biến đó.

let myVariable;

myVariable;
> undefined

myVariable = 5;

myVariable + myVariable
> 10

Biến có giá trị undefined khác với biến chưa xác định có giá trị nhận dạng chưa được khai báo. Việc tham chiếu đến một biến mà bạn chưa khai báo sẽ gây ra lỗi.

myVariable
> Uncaught ReferenceError: myVariable is not defined

let myVariable;

myVariable
> undefined

Việc liên kết một giá trị với một giá trị nhận dạng thường được gọi là "liên kết". Cú pháp theo sau từ khoá let, var hoặc const được gọi là "danh sách liên kết" và cho phép nhiều nội dung khai báo biến được phân tách bằng dấu phẩy (kết thúc bằng dấu chấm phẩy dự kiến). Điều này khiến các đoạn mã sau đây giống hệt nhau về chức năng:

let firstVariable,
     secondVariable,
     thirdVariable;
let firstVariable;
let secondVariable;
let thirdVariable;

Việc gán lại giá trị của biến không sử dụng let (hoặc var), vì JavaScript đã biết biến đó tồn tại:

let myVariable = true;

myVariable
> true

myVariable = false;

myVariable
> false

Bạn có thể gán lại giá trị mới cho biến dựa trên giá trị hiện có của biến:

let myVariable = 10;

myVariable
> 10

myVariable = myVariable * myVariable;

myVariable
> 100

Nếu cố gắng khai báo lại một biến bằng let trong môi trường phát hành chính thức, bạn sẽ gặp lỗi cú pháp:

let myVariable = true;
let myVariable = false;
> Uncaught SyntaxError: redeclaration of let myVariable

Công cụ dành cho nhà phát triển của trình duyệt cho phép khai báo lại let (và class) nhiều hơn, vì vậy, bạn có thể không thấy lỗi tương tự trong bảng điều khiển dành cho nhà phát triển.

Để duy trì khả năng tương thích với trình duyệt cũ, var cho phép khai báo lại không cần thiết mà không gặp lỗi trong bất kỳ ngữ cảnh nào:

var myVariable = true;
var myVariable = false;

myVariable\
> false

const

Sử dụng từ khoá const để khai báo hằng số, một loại biến phải được khởi tạo ngay lập tức và sau đó không thể thay đổi. Giá trị nhận dạng cho hằng số tuân theo tất cả các quy tắc giống như các biến được khai báo bằng let (và var):

const myConstant = true;

myConstant
> true

Bạn không thể khai báo một hằng số mà không chỉ định giá trị cho hằng số đó ngay lập tức, vì bạn không thể chỉ định lại hằng số sau khi tạo. Vì vậy, mọi hằng số chưa khởi tạo sẽ mãi mãi là undefined. Nếu cố gắng khai báo một hằng số mà không khởi tạo hằng số đó, bạn sẽ gặp lỗi cú pháp:

const myConstant;
Uncaught SyntaxError: missing = in const declaration

Việc cố gắng thay đổi giá trị của một biến được khai báo bằng const theo cách bạn có thể thay đổi giá trị của một biến được khai báo bằng let (hoặc var) sẽ gây ra lỗi loại:

const myConstant = true;

myConstant = false;
> Uncaught TypeError: invalid assignment to const 'myConstant'

Tuy nhiên, khi một hằng số được liên kết với một đối tượng, các thuộc tính của đối tượng đó có thể bị thay đổi.

const constantObject = { "firstvalue" : true };

constantObject
> Object { firstvalue: true }

constantObject.secondvalue = false;

constantObject
> Object { firstvalue: true, secondvalue: false }

Hằng số chứa một đối tượng là một tham chiếu bất biến đến giá trị dữ liệu có thể thay đổi. Mặc dù không thể thay đổi chính hằng số, nhưng bạn có thể thay đổi, thêm hoặc xoá các thuộc tính của đối tượng được tham chiếu:

const constantObject = { "firstvalue" : true };

constantObject = false
> Uncaught TypeError: invalid assignment to const 'constantObject'

Khi bạn không muốn gán lại một biến, tốt nhất bạn nên đặt biến đó thành hằng số. Việc sử dụng const sẽ cho nhóm phát triển hoặc người bảo trì dự án trong tương lai biết rằng họ không được thay đổi giá trị đó để tránh vi phạm các giả định mà mã của bạn đưa ra về cách sử dụng giá trị đó, ví dụ: một biến cuối cùng sẽ được đánh giá theo loại dữ liệu dự kiến.

Phạm vi biến

Phạm vi của biến là phần của tập lệnh có chứa biến đó. Bên ngoài phạm vi của biến, biến sẽ không được xác định – không phải là giá trị nhận dạng chứa giá trị undefined, mà như thể biến chưa được khai báo.

Tuỳ thuộc vào từ khoá bạn sử dụng để khai báo biến và ngữ cảnh mà bạn xác định biến, bạn có thể đặt phạm vi biến cho câu lệnh chặn (phạm vi chặn), hàm riêng lẻ (phạm vi hàm) hoặc toàn bộ ứng dụng JavaScript (phạm vi toàn cục).

Phạm vi khối

Bất kỳ biến nào bạn khai báo bằng let hoặc const đều nằm trong phạm vi của câu lệnh khối gần nhất chứa biến đó, nghĩa là bạn chỉ có thể truy cập biến trong khối đó. Việc cố gắng truy cập vào một biến có phạm vi khối bên ngoài khối chứa của biến đó sẽ gây ra lỗi tương tự như khi cố gắng truy cập vào một biến không tồn tại:

{
    let scopedVariable = true;
    console.log( scopedVariable );
}
> true

scopedVariable
> ReferenceError: scopedVariable is not defined

Đối với JavaScript, biến trong phạm vi khối không tồn tại bên ngoài khối chứa biến đó. Ví dụ: bạn có thể khai báo một hằng số bên trong một khối, sau đó khai báo một hằng số khác bên ngoài khối đó sử dụng cùng một giá trị nhận dạng:

{
  const myConstant = false;
}
const myConstant = true;

scopedConstant;
> true

Mặc dù biến đã khai báo không thể mở rộng vào khối mẹ, nhưng biến này sẵn cho tất cả các khối con:

{
    let scopedVariable = true;
    {
    console.log( scopedVariable );
    }
}
> true

Bạn có thể thay đổi giá trị của một biến đã khai báo từ bên trong một khối con cháu:

{
    let scopedVariable = false;
    {
    scopedVariable = true;
    }
    console.log( scopedVariable );
}
> true

Bạn có thể khởi tạo biến mới bằng let hoặc const bên trong khối con mà không gặp lỗi, ngay cả khi biến đó sử dụng cùng một giá trị nhận dạng với biến trong khối mẹ:

{
    let scopedVariable = false;
    {
    let scopedVariable = true;
    }
    console.log( scopedVariable );
}
> false

Phạm vi hàm

Các biến được khai báo bằng var nằm trong phạm vi của hàm chứa gần nhất (hoặc khối khởi tạo tĩnh bên trong một lớp).

function myFunction() {
    var scopedVariable = true;

    return scopedVariable;
}

scopedVariable;
> ReferenceError: scopedVariable is not defined

Điều này vẫn xảy ra sau khi một hàm được gọi. Mặc dù biến được khởi tạo trong khi hàm thực thi, nhưng biến đó vẫn không có sẵn bên ngoài phạm vi của hàm:

function myFunction() {
    var scopedVariable = true;

    return scopedVariable;
}

scopedVariable;
> ReferenceError: scopedVariable is not defined

myFunction();
> true

scopedVariable;
> ReferenceError: scopedVariable is not defined

Phạm vi toàn cục

Biến toàn cục có sẵn trong toàn bộ ứng dụng JavaScript, bên trong mọi khối và hàm, cho mọi tập lệnh trên trang.

Mặc dù đây có vẻ là một giá trị mặc định mong muốn, nhưng các biến mà bất kỳ phần nào của ứng dụng cũng có thể truy cập và sửa đổi có thể làm tăng thêm chi phí không cần thiết hoặc thậm chí gây ra xung đột với các biến ở nơi khác trong ứng dụng có cùng giá trị nhận dạng. Điều này áp dụng cho mọi JavaScript liên quan đến việc hiển thị trang, bao gồm cả các thư viện của bên thứ ba và số liệu phân tích người dùng. Do đó, tốt nhất bạn nên tránh làm ô nhiễm phạm vi toàn cục bất cứ khi nào có thể.

Bất kỳ biến nào được khai báo bằng var bên ngoài hàm mẹ hoặc sử dụng let hoặc const bên ngoài khối mẹ đều là biến toàn cục:

var functionGlobal = true; // Global
let blockGlobal = true; // Global

{
    console.log( blockGlobal );
    console.log( functionGlobal );
}
> true
> true

(function() {
    console.log( blockGlobal );
    console.log( functionGlobal );
}());
> true
> true

Việc gán giá trị cho một biến mà không khai báo rõ ràng biến đó (tức là không bao giờ sử dụng var, let hoặc const để tạo biến) sẽ nâng biến lên phạm vi toàn cục, ngay cả khi được khởi tạo bên trong một hàm hoặc khối. Biến được tạo bằng mẫu này đôi khi được gọi là "biến toàn cục ngầm ẩn".

function myFunction() {
    globalVariable = "global";

    return globalVariable
}

myFunction()\
> "global"

globalVariable\
> "global"

Chuyển biến lên trên

Các biến và phần khai báo hàm được chuyển lên trên đầu phạm vi, nghĩa là trình thông dịch JavaScript xử lý mọi biến được khai báo tại bất kỳ điểm nào trong tập lệnh và di chuyển biến đó một cách hiệu quả lên dòng đầu tiên của phạm vi bao bọc trước khi thực thi tập lệnh. Điều này có nghĩa là bạn có thể tham chiếu đến một biến được khai báo bằng var trước khi khai báo biến mà không gặp lỗi:

hoistedVariable
> undefined

var hoistedVariable;

Vì chỉ có phần khai báo biến được lưu trữ, chứ không phải phần khởi chạy, nên các biến chưa được khai báo rõ ràng bằng var, let hoặc const sẽ không được chuyển lên trên:

unhoistedVariable;
> Uncaught ReferenceError: unhoistedVariable is not defined

unhoistedVariable = true;

Như đã đề cập trước đó, một biến đã khai báo nhưng chưa khởi tạo sẽ được gán giá trị undefined. Hành vi đó cũng áp dụng cho các phần khai báo biến được chuyển lên trên, nhưng chỉ áp dụng cho các phần khai báo được khai báo bằng var.

hoistedVariable
> undefined

var hoistedVariable = 2 + 2;

hoistedVariable\
> 4

Hành vi không trực quan này chủ yếu là do các quyết định thiết kế được đưa ra trong các phiên bản JavaScript đầu tiên và không thể thay đổi nếu không có nguy cơ làm hỏng các trang web hiện có.

letconst giải quyết hành vi này bằng cách gửi lỗi khi truy cập vào một biến trước khi tạo biến đó:

{
    hoistedVariable;

    let hoistedVariable;
}
> Uncaught ReferenceError: can't access lexical declaration 'hoistedVariable' before initialization

Lỗi này khác với lỗi "hoistedVariable is not defined" (hoistedVariable không được xác định) mà bạn có thể gặp phải khi cố gắng truy cập vào một biến chưa khai báo. Vì JavaScript đã chuyển biến lên trên, nên nó biết rằng biến sẽ được tạo trong phạm vi đã cho. Tuy nhiên, thay vì cung cấp biến đó trước khi khai báo với giá trị undefined, trình thông dịch sẽ gửi một lỗi. Các biến được khai báo bằng let hoặc const (hoặc class) được cho là tồn tại trong "vùng chết tạm thời" ("TDZ") từ đầu khối bao bọc cho đến điểm trong mã nơi biến được khai báo.

Vùng chết tạm thời giúp hành vi của let trực quan hơn so với var đối với tác giả. Điều này cũng rất quan trọng đối với thiết kế của const. Vì không thể thay đổi hằng số, nên hằng số được chuyển lên đầu phạm vi và được cung cấp giá trị ngầm undefined không thể được khởi tạo bằng một giá trị có ý nghĩa.

Kiểm tra mức độ hiểu biết

Bạn có thể bắt đầu giá trị nhận dạng bằng những loại ký tự nào?

Một bức thư
Dấu gạch dưới
Một chữ số

Phương thức nào được ưu tiên để khai báo một biến có giá trị có thể thay đổi bất cứ lúc nào?

let
const
var