Biến

Biến là một cấu trúc dữ liệu chỉ định tên đại diện cho một giá trị. Chúng 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, ký hiệu đô la ($), ký tự dấu gạch dưới (_), chữ số (0-9) và thậm chí là 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 2 giá trị nhận dạng (myVariable) và sẽ gửi lỗi cú pháp ("mã nhận dạng không mong muốn: mã 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 ký hiệ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 các số ở đầu giá trị nhận dạng, thì điều này sẽ cho phép giá trị nhận dạng chỉ gồm các chữ số, gây ra xung đột giữa các số được dùng dưới dạng số và các số được 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 (! . , / \ + - * =).

Những quy tắc sau đây không phải là quy tắc nghiêm ngặt để tạo giá trị nhận dạng, nhưng 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ó nhiều tiêu chuẩn, hãy làm 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, kiểu lạc đà (còn được cách điệu là "kiểu lạc đà") là một quy ước rất phổ biến cho giá trị nhận dạng được tạo thành từ 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 có nhiều từ thường sử dụng một biến thể viết hoa kiểu lạc đà, thường được gọi là "viết hoa lạc đà trên" hoặc 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 có trong đó (ví dụ: currentMonthDays là tên phù hợp hơn theNumberOfDaysInTheCurrentMonth) và đọc nhanh một cách rõ ràng (originalValue tốt hơn val). Giá trị nhận dạng myVariable dùng trong mô-đun này hoạt động trong bối cảnh các ví dụ tách biệt, nhưng sẽ rất không hữu ích trong mã phát hành chính thức vì chúng không cung cấp thông tin về dữ liệu có trong đó.

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

JavaScript không cung cấp đặ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ị này thường được dùng để cho thấy rằng một biến, phương thức hoặc thuộc tính là "riêng tư", nghĩa là chỉ nhằm mục đích sử dụng trong ngữ cảnh của đối tượng chứa đối tượng đó. Bạn không nên 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 việc thêm các thuộc tính riêng tư của JavaScript.

Khai báo biến

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

let myVariable;

Hãy 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 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 rõ ràng đã được xác định trong các phiên bản JavaScript đầu tiên và sau này không thể thay đổi được để 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 chỉ định một giá trị cho biến đó. Sử dụng một dấu bằng (=) để chỉ định hoặc gán 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 một biến bằng let (hoặc var) mà không cần khởi tạo biến đó ngay. Nếu bạn thực hiện việc này, giá trị ban đầu của biến là undefined cho đến khi mã của bạn gán cho biến đó một giá trị.

let myVariable;

myVariable;
> undefined

myVariable = 5;

myVariable + myVariable
> 10

Biến có giá trị undefined khác với một biến không xác định có giá trị nhận dạng chưa được khai báo. Việc tham chiếu 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 giá trị nhận dạng với một giá trị thường được gọi là "liên kết". Cú pháp theo sau các từ khoá let, var hoặc const được gọi là "danh sách liên kết" và cho phép khai báo nhiều 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). Nhờ vậy, các đoạn mã sau đây có chức năng giống hệt nhau:

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

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

let myVariable = true;

myVariable
> true

myVariable = false;

myVariable
> false

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

let myVariable = 10;

myVariable
> 10

myVariable = myVariable * myVariable;

myVariable
> 100

Nếu cố 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ụ cho nhà phát triển của trình duyệt cho phép các trình duyệt khai báo lại let (và class), 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 của trình duyệt cũ, var cho phép khai báo lại một cách 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ố, 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 mọi quy tắc tương tự 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 hằng số mà không chỉ định giá trị cho hằng số đó ngay lập tức, vì hằng số không thể chỉ định lại sau khi được tạo, do đó, mọi hằng số chưa khởi tạo sẽ vẫn là undefined vĩnh viễn. Nếu cố gắng khai báo 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 biến được khai báo bằng const theo cách bạn có thể thay đổi giá trị của 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, bạn có thể thay đổi thuộc tính của đối tượng đó.

const constantObject = { "firstvalue" : true };

constantObject
> Object { firstvalue: true }

constantObject.secondvalue = false;

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

Hằng số chứa đối tượng là một tham chiếu không thể thay đổi đến giá trị dữ liệu có thể thay đổi. Mặc dù bạn không thể thay đổi hằng số, nhưng bạn có thể thay đổi, thêm hoặc loại bỏ 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 một biến được chỉ định lại, tốt nhất là bạn nên đặt biến đó thành hằng số. Việc sử dụng const sẽ yêu cầu nhóm phát triển hoặc những người bảo trì trong tương lai của dự án không thay đổi giá trị đó, để tránh phá vỡ những giả định mà mã của bạn đưa ra về cách sử dụng, ví dụ: một biến cuối cùng sẽ được đánh giá dựa trên loại dữ liệu dự kiến.

Phạm vi biến

Phạm vi của biến là một phần của tập lệnh nơi biến đó có sẵn. Bên ngoài phạm vi của một biến, biến sẽ không được xác định, chứ 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 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 cho các biến cần chặn để chặn câu lệnh (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).

Chặn phạm vi

Mọi biến 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 chứa gần nhất, nghĩa là biến chỉ có thể truy cập được trong khối đó. Việc cố gắng truy cập vào một biến trong phạm vi khối bên ngoài khối chứa của nó sẽ gây ra lỗi tương tự như khi cố 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

Theo 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 sang 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 biến đã khai báo từ bên trong khối con cháu:

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

Bạn có thể khởi tạo một biến mới bằng let hoặc const bên trong một khối con cháu 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 như một biến trong khối mẹ:

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

Phạm vi của hàm

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

function myFunction() {
    var scopedVariable = true;

    return scopedVariable;
}

scopedVariable;
> ReferenceError: scopedVariable is not defined

Trường hợp này vẫn xảy ra sau khi gọi một hàm. 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 sử dụng được 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 trên toàn cầu

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

Mặc dù đây có vẻ là 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ó thể truy cập và sửa đổi có thể làm tăng mức hao tổn không cần thiết hoặc thậm chí gây 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 quá trình hiển thị một trang, bao gồm cả những nội dung như thư viện của bên thứ ba và số liệu phân tích người dùng. Do đó, phương pháp hay nhất là tránh làm ô nhiễm phạm vi toàn cục bất cứ khi nào có thể.

Mọi biến được khai báo bằng var bên ngoài hàm mẹ hoặc sử dụng let hay 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 chỉ định giá trị cho một biến mà không khai báo rõ ràng (nghĩa 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. Một biến được tạo bằng mẫu này đôi khi được gọi là "toàn cục ngụ ý".

function myFunction() {
    globalVariable = "global";

    return globalVariable
}

myFunction()\
> "global"

globalVariable\
> "global"

Chuyển biến

Biến và nội dung khai báo hàm được chuyển lên đầu phạm vi, nghĩa là trình thông dịch JavaScript sẽ 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 hiệu quả biến đó đế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à một biến được khai báo bằng var có thể được tham chiếu trước khi khai báo biến đó mà không gặp lỗi:

hoistedVariable
> undefined

var hoistedVariable;

Vì chỉ phần khai báo biến mới được lưu trữ, chứ không phải quá trình 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ị là undefined. Hành vi đó cũng áp dụng cho các nội dung khai báo biến được chuyển lên trên, nhưng chỉ áp dụng cho những nội dung 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à ảnh hưởng đến 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 xử lý hành vi này bằng cách gửi lỗi khi truy cập 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 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 được khai báo. Vì JavaScript đã nâng biến này lên trên, nên nhận biết rằng biến sẽ được tạo trong phạm vi nhất định. Tuy nhiên, thay vì cung cấp biến đó trước khi khai báo bằng giá trị undefined, trình thông dịch sẽ gửi 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") kể từ đầu khối bao quanh cho đến điểm trong mã mà biến được khai báo.

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

Kiểm tra kiến thức

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

Chữ cái
Dấu gạch dưới
Một chữ số

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

hãy
const
var