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 bất kỳ loại dữ liệu nào.

Tên của biến được gọi là mã 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 ($), dấu gạch dưới ký tự (_), 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 để các phần tử đầu vào riêng biệt. Ví dụ: nếu bạn cố 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, myVariable đồng thời gửi lỗi cú pháp ("mã thông báo không mong muốn: ").
  • 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 ($). Chú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 đó sẽ cho phép giá trị nhận dạng chỉ bao gồm các số, gây ra xung đột giữa các số được dùng làm số và số được dùng làm giá trị nhận dạng:

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

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

Dưới đây không phải là các quy tắc nghiêm ngặt để tạo giá trị nhận dạng, nhưng các phương pháp hay nhất trong ngành giúp duy trì mã dễ dàng hơn. Nếu dự án có các tiêu chuẩn khác nhau, hãy áp dụng 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, viết hoa Camel (còn được cách điệu là "camelCase") là một quy ước rất phổ biến đối với giá trị nhận dạng được tạo thành từ nhiều từ. Nguyên tắc lạc đà là phương pháp viết hoa chữ cái đầu tiên của mỗi từ, ngoại trừ chữ cái đầu tiên của từ được cải thiện dễ đọc mà không có 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 bối cảnh và tính 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ể của camel viết hoa thường được gọi là "viết hoa kiểu lạc đà trên" hoặc Kiểu viết hoa 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 giá trị nhận dạng (đối với ví dụ: currentMonthDays là tên hay hơn theNumberOfDaysInTheCurrentMonth) và đọc nhanh và rõ ràng (originalValue tốt hơn val). Chiến lược phát hành đĩa đơn 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ụ 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 chứa trong đó.

Giá trị nhận dạng không nên quá cụ thể về dữ liệu có trong giá trị nhận dạng, vì giá trị của chúng có thể thay đổi tùy thuộc vào cách tập lệnh hoạt động dựa trên dữ liệu đó hoặc quyết định mà những người duy trì trong tương lai đưa ra. Ví dụ: ban đầu biến đã cho có thể cần thay đổi mã nhận dạng miles thành một giá trị tính bằng km trong tương lai dự án, yêu cầu nhà bảo trì thay đổi bất kỳ tham chiếu nào đến biến đó thành để 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 của bạn thay thế.

JavaScript không 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 các ký tự dấu gạch dưới (_), nhưng các ký tự này thường được sử dụng để cho biết rằng biến, phương thức hoặc thuộc tính là "riêng tư" có nghĩa là mục đích để sử dụng trong ngữ cảnh của đối tượng chứa đối tượng đó, và không nên được truy cập hoặc sửa đổi bên ngoài ngữ cảnh đó. Đây là một quy ước được chuyển sang từ các ngôn ngữ lập trình khác và có trước việc bổ sung đoạn mã tài sản riêng tư.

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" một biến. Một biến được khai báo bằng let, const, hoặc var từ khoá.

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 các từ khoá cho trình thông dịch JavaScript biết rằng một chuỗi ký tự là mã nhận dạng có thể chứa giá trị.

Khi làm việc trong một 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 thành 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ới thiết kế var.

Một biến đã khai báo được khởi tạo bằng cách gán một giá trị cho biến đó. Sử dụng dấu bằng đơn (=) để gán hoặc gán lại một giá trị cho một biến. Bạn có thể điều này dưới dạng một phần của cùng một tuyên bố khai báo điều đó:

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 lập tức. Nếu bạn làm như vậy, giá trị ban đầu của biến này sẽ là undefined cho đến khi mã sẽ chỉ định một giá trị cho đoạn mã đó.

let myVariable;

myVariable;
> undefined

myVariable = 5;

myVariable + myVariable
> 10

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

myVariable
> Uncaught ReferenceError: myVariable is not defined

let myVariable;

myVariable
> undefined

Mối liên kết giữa một mã nhận dạng với một giá trị thường được gọi là "liên kết". Cú pháp tuân theo 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). Thao tác này sẽ tạo các đoạn mã sau giống hệt nhau về chức năng:

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 tồn tại:

let myVariable = true;

myVariable
> true

myVariable = false;

myVariable
> false

Bạn có thể chỉ định lại biến giá trị mới 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 bạn 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

Trình duyệt công cụ cho nhà phát triển thoải mái hơn trong việc khai báo lại let (và class), nên bạn có thể không gặp 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 một cách không cần thiết không có lỗi trong bất kỳ ngữ cảnh nào:

var myVariable = true;
var myVariable = false;

myVariable\
> false

const

Dùng từ khoá const để khai báo hằng số (một loại biến bắt buộc phải là) được khởi tạo ngay lập tức và sau đó không thay đổi được. Giá trị nhận dạng cho hằng số tuân theo tất cả các 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 một hằng số mà không chỉ định ngay một giá trị cho hằng số đó vì hằng số sẽ không được chỉ định lại sau khi tạo, vì vậy, mọi hằng số chưa khởi tạo hằng số sẽ ở undefined vĩnh viễn. Nếu bạn cố gắng khai báo một hằng số mà không khởi chạy thuộc tính này, 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 mà bạn có thể thay đổi giá trị của một biến được khai báo bằng let (hoặc var) dẫn đến một loại lỗ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ể thay đổi đố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 một đối tượng là một hằng số không thể thay đổi tham chiếu đến giá trị dữ liệu có thể thay đổi. Mặc dù hằng số đó không thể thay đổi nhưng các thuộc tính của tham số đó sẽ không thể thay đổi có thể thay đổi, thêm vào hoặc xoá đối tượng:

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 đó hằng số. Việc sử dụng const giúp nhóm phát triển hoặc những người bảo trì trong tương lai biết về dự án không thay đổi giá trị đó để tránh phá vỡ các giả định trong mã của bạn về cách sử dụng biến đó, ví dụ: một biến cuối cùng sẽ được đượ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 mà biến đó khả dụng. Biến ngoài phạm vi của biến sẽ không được xác định, chứ không phải dưới dạng giá trị nhận dạng chứa giá trị undefined, nhưng như thể giá trị này chưa được khai báo.

Tuỳ thuộc vào từ khoá mà bạn dùng để khai báo một biến và ngữ cảnh mà theo đó bạn xác định nó, bạn có thể xác định phạm vi biến để chặn câu lệnh (phạm vi khối), 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ầu).

Phạm vi chặn

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

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

scopedVariable
> ReferenceError: scopedVariable is not defined

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

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

scopedConstant;
> true

Mặc dù một biến đã khai báo không thể mở rộng vào khối mẹ, nhưng biến đó có 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 ngay từ bên trong khối con:

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

Một biến mới có thể được khởi tạo bằng let hoặc const bên trong một thành phần con mà không có lỗi, ngay cả khi khối đó sử dụng cùng một giá trị nhận dạng với một 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 đều nằm trong phạm vi hàm chứa gần nhất (hoặc khối khởi động 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 một hàm được gọi. Mặc dù được khởi tạo trong khi hàm thực thi, nên biến đó vẫn được không hoạt động bên ngoài phạm vi 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ầ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à tất cả khối và hàm, vào bất kỳ tập lệnh nào trên trang.

Mặc dù đây có vẻ như là một giá trị mặc định mong muốn, nhưng 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 chi phí không cần thiết hoặc thậm chí khiến 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 bất kỳ và tất cả JavaScript liên quan đến việc hiển thị một trang, bao gồm những thứ như thư viện của bên thứ ba và số liệu phân tích người dùng. Do đó, để 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 bằng let hay const bên ngoài khối mẹ, là 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

Gán một giá trị cho biến mà không khai báo rõ ràng (nghĩa là chỉ định không bao giờ sử dụng var, let hoặc const để tạo) sẽ nâng một biến lên toàn cầu, 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à "ngụ ý toàn cục".

function myFunction() {
    globalVariable = "global";

    return globalVariable
}

myFunction()\
> "global"

globalVariable\
> "global"

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

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

hoistedVariable
> undefined

var hoistedVariable;

Vì chỉ lưu trữ phần khai báo biến chứ không phải phần khởi tạo, các biến chưa được khai báo rõ ràng bằng var, let hoặc const không được di 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 được khởi tạo được gán một giá trị undefined. Hành vi đó áp dụng cho biến được chuyển lên trên mà chỉ áp dụng cho những khai báo được khai báo bằng var.

hoistedVariable
> undefined

var hoistedVariable = 2 + 2;

hoistedVariable\
> 4

Hành vi khó hiểu này phần lớn là sự cản trở của các quyết định thiết kế được đưa ra trong các phiên bản JavaScript sớm nhất, và không thể thay đổi được nếu không có rủi ro làm hỏng trang web hiện có.

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

{
    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" lỗi, bạn có thể xảy ra khi cố gắng truy cập vào một biến chưa được khai báo. Vì JavaScript đã chuyển biến lên trên, thì biết rằng biến sẽ được tạo trong phạm vi nhất định. Tuy nhiên, thay vì tạo biến đó trước có giá trị undefined thì 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 xem là tồn tại trong một "vùng chết tạm thời" ("TDZ") từ đầu khối bao quanh cho đến khi đ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ì hằng số không thể là đã thay đổi, một hằng số được chuyển lên đầu phạm vi và được cấp một giá trị ngầm ẩn sau đó không thể khởi chạy 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 những loại ký tự nào?

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

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

cho phép
const
biến