Cơ bản

Tram Ho

A. JS introduction

1. Introduction

JavaScript (JS) là một ngôn ngữ lập trình client side, được dùng trong trang web bên cạnh HTML và CSS. HTML dùng tạo bộ xương cho trang web, CSS trang trí và tạo layout cho trang web lung linh hơn, thì JS lập trình để có những hành vi, tương tác với người dùng.

Hiện nay, JS không chỉ có thể viết client side mà còn mở rộng ra nhiều lĩnh vực khác, có thể kể tới như viết app với Electron hoặc web service với Node.js. Các thư viện, framework cho JS phát triển rất mạnh, như jquery, React, Angular, Vue,…

Lưu ý, JS không phải Java.

Trong series này chỉ nói tới JS ở khía cạnh cơ bản nhất, là tạo tương tác cho trang web. JS được chạy trong trình duyệt và hầu hết trình duyệt hiện nay đều support tốt JS.

JS là ngôn ngữ có phân biệt hoa thường.

2. Đặt code ở đâu?

Script tag trong HTML

Code JS có thể được đặt trong một tag script, và HTML document có thể chứa nhiều script như vậy.

Các đoạn code JS trong cùng document có thể được gọi, sử dụng lẫn nhau.

External JS

Code JS cũng có thể chứa trong một file riêng, và được liên kết vào HTML cũng bằng thẻ script.

Tag script lúc này không có nội dung, và chỉ có thuộc tính src trỏ tới URL chứa tệp script.js.

Nên sử dụng external JS, vì phân chia code tốt hơn và tăng tốc độ load trang web vì tệp external có thể được cache.

HTML event

Các thuộc tính event của các HTML tag cũng có thể chứa script. Và code được gọi khi event xảy ra (fire – được bắn ra).

Code JS trong event dạng này thường chỉ dùng để gọi các function đã định nghĩa trong các script khác (script hoặc external js), chứ không phù hợp cho các đoạn code quá dài.

Head, or body?

Script có thể đặt trong cả head lẫn body. Trước đây người ta khuyến khích đặt ở cuối cùng của body, như sau để cải thiện tốc độ tải trang (vì hạn chế script làm delay việc hiển thị).

Tuy nhiên, nhờ có thêm thuộc tính defer của tag script nên ta không còn bận tâm vấn đề này nữa. Nhờ có defer, code JS sẽ chỉ thực thi khi trang web đã phân tích xong. Chú ý defer chỉ dành cho tệp JS external.

3. Output

Có nhiều cách để đưa kết quả ra màn hình để xem. Ví dụ như xem value của biến chẳng hạn. Giống như C++ dùng cout, Java dùng System.out.print() thì JS cũng có những cách riêng để hiển thị output ra bên ngoài.

Console.log

Đưa output ra console của chrome devtools. Cách này khá nhanh và cũng tiện, nó tương tự cout trong C++. Nhược điểm duy nhất là bạn phải nhấn F12 để mở console ra xem.

Mỗi lần gọi, console sẽ in ra theo từng dòng nên bạn không cần quan tâm vấn đề xuống dòng. Và nếu in liên tiếp nhiều giá trị, thì console tự động thêm space tách chúng ra. Bạn không cần làm thủ công.

Tùy vào loại dữ liệu, khi đưa ra console sẽ được hiển thị khác nhau. Và một điểm rất hay là những loại phức tạp như array, object sẽ được hiển thị trực quan, đầy đủ thông tin cần thiết. Ví dụ, thay vì dùng vòng lặp để in ra mảng như sau.

Thì chỉ cần

Console sẽ hiển thị đầy đủ dữ liệu, không chỉ có các phần tử mà còn gồm độ dài mảng. Đối với object thì là các key và value thuộc tính.

Document.write

Dùng để ghi một đoạn text, hoặc một đoạn HTML ra trang web.

Nhược điểm là phải ghi đúng format, nếu muốn xuống dòng phải thêm chuỗi <br>. Và chú ý quan trọng, không dùng method này khi trang web tải xong, vì nó sẽ xóa toàn bộ những gì đang có và write lại từ đầu.

Write to HTML element

Ngoài ra còn có thể đưa output ra một element nào đó. Ví dụ bạn có một thẻ p, và bạn đưa output ra thẻ này (làm nó hiển thị nội dung output), và bạn có thể đọc được nhờ sự thay đổi trên trang web.

Nhược điểm cách này là khá rườm rà, phải “select” các element, rồi thay đổi thuộc tính innerHTML của nó. Nó yêu cầu phải tạo thêm một element, với id hay cái gì đó để xác định, nên gây phức tạp cho chương trình.

Window.alert()

Ngoài ra có thể dùng method alert() để đưa ra một popup thông báo. Nhưng không được dùng nhiều khi mới học code.

Nhược điểm là không hiển thị nhiều dữ liệu được, và không hiển thị cùng lúc (vì bị chặn).

B. JS basic

1. Statement

Statement hiểu là một câu lệnh. Trong chương trình có nhiều lệnh, và cuối mỗi lệnh nên đặt dấu chấm phẩy để tách ra. Nếu không đặt cũng không sao, thường thì JS không bắt buộc, chỉ khi bạn viết nhiều lệnh trên cùng một dòng thì nên dùng chấm phẩy tách chúng ra. Ví dụ.

Mặc dù không cần chấm phẩy, các lệnh trên vẫn hoạt động bình thường, vì nằm trên các dòng khác nhau. Nhưng code sau là sai.

Quy tắc chúng là nên thêm chấm phấy cuối mỗi lệnh, để tránh rắc rối như sau.

JS sẽ hiểu code trên là như sau, dẫn tới việc sai kết quả.

2. Comment

Comment bị bỏ qua khi thực thi JS, và có thể dùng comment để giải thích code cho dễ hiểu hơn.

JS có 2 loại comment là single line comment và multi-line comment (block).

Comment dạng 1 comment từ vị trí dấu // cho tới hết dòng. Comment dạng 2 có thể trải rộng trên nhiều dòng, chỉ cần nằm trong cặp /* */ sẽ được coi là comment.

Single line comment phổ biến hơn, trong khi block comment thường dùng trong documentation.

3. Variable, constant

Có hai từ khóa khai báo biến là let và var. Vì JS là ngôn ngữ weaktype nên không cần chỉ định rõ kiểu của biến, mà được tự động xác định và thay đổi dựa theo value (giá trị) chứa bên trong.

Biến không khởi tạo giá trị sẽ có value là undefined.

Mặc dù cả let và var đều dùng khai báo biến được, nhưng nên dùng let hơn. Chi tiết sẽ được đề cập trong phần cuối chương này.

Biến có thể khai báo trên nhiều dòng, hoặc nhiều biến trên một dòng đều đ

Đối với hằng số (constant) thì dùng từ khóa const như trên để khai báo.

4. Operators

Toán tử (operator) là những phép tính thực hiện trên các số (biến, hằng, giá trị,…) gọi là các toán hạng (operand). Kết hợp của toán tử và toán hạng tạo ra những biểu thức (expression) và trả về giá trị nào đó.

Tùy số lượng toán hạng tham gia, được chia thành 3 loại toán tử chính:

  • Toán tử một ngôi (unary operator): chỉ có một toán hạng, gồm phép thuận +x, phép đối -x và phủ định !x.
  • Toán tử hai ngôi (binary operator): có hai toán hạng, và hầu hết các phép tính số học đều là hai ngôi, như phép cộng a + b,…
  • Toán tử ba ngôi (ternary operator): nhận 3 toán hạng, dựa vào toán hạng 1 mà lựa chọn trả về toán hạng 2 hoặc 3.

Ngoài ra còn có các toán tử khác, như gán (assignment), logic (logical), so sánh (comparison), thao tác bit (bitwise),…

Arithmetic operators

Các phép toán số học trong JS tương tự các ngôn ngữ khác, gồm:

  • Toán tử một ngôi: +x, lấy số đối -x, tăng ++x, x++, giảm --x, x--.
  • Toán tử hai ngôi: cộng a + b, trừ a - b, nhân a * b, chia nguyên a / b, chia dư a & b.

Ngoài ra từ phiên bản ES6 trở đi có phép lũy thừa a ** b để tính số mũ nhanh chóng.

Assignment operators

Toán tử gán x = 5 tương tự các ngôn ngữ khác. Ngoài ra JS cũng hỗ trợ các phép gán rút gọn như x += 5, x /= 5,…

Comparison operators

Gồm các phép so sánh bằng x == 5, khác x != 10 lớn bé, lớn hơn hoặc bằng, bé hơn hoặc bằng như trong các ngôn ngữ khác.

Các toán tử comparison luôn trả về kết quả boolean.

Ngoài ra JS còn có hai phép so sánh mới là strict comparison x === 10x !== 10. Điểm khác biệt ở chỗ strict comparison yêu cầu hai vế phải cùng kiểu dữ liệu thì mới so sánh, trong khi loose comparison x == 10 tự động convert kiểu phù hợp rồi mới so sánh.

Logical operators

Dùng cho các toán hạng boolean. Gồm and a && b, or a || b và not !a.

Bitwise operators

Dùng cho các phép thao tác bit, gồm hai dạng.

Dạng 1 dùng biến đổi bit, gồm 3 toán tử hai ngôi (and x & y, or x | y, xor x ^ y) và một toán tử một ngôi (not !x).

Chú ý các phép trên chỉ sử dụng một dấu, thay vì hai dấu như comparison.

Loại thứ 2 dùng dịch chuyển bit (bit shifting), gồm left shift (zero fill) x << n, signed right shift x >> n và zero fill right shift x >>> n.

Trong JS thì thao tác bit ít dùng.

Type operators

Gồm hai toán tử là typeof (lấy kiểu của dữ liệu) và instanceof (kiểm tra instance).

Operator precedence

Thường thì độ ưu tiên các toán tử giống bên toán học, lập trình có những quy tắc khác phức tạp hơn nhưng vẫn giữ cơ bản như vậy.

Chúng ta không cần nhớ tất cả những thứ tự rườm rà đó, khi nào thấy rắc rối cứ dùng ngoặc () bao lại, vừa an toàn vừa dễ hiểu.

C. Data types

1. Overview

Kiểu dữ liệu (data type) dùng định nghĩa loại dữ liệu mà biến (hoặc hằng) lưu trữ. JS là một ngôn ngữ weak type, nên kiểu dữ liệu không được chỉ định rõ ràng, kiểu được tự động xác định dựa trên giá trị (value) mà nó chứa.

Kiểu dữ liệu của một biến trong JS có thể thay đổi, điều này là không được phép trong các ngôn ngữ strong type.

Trong JS có một số kiểu như sau:

  • Number: chứa số nguyên và thực
  • Boolean: đúng sai
  • String: chuỗi
  • Object: đối tượng
  • Null
  • Undefined

ES6 bổ sung thêm kiểu Symbol nữa. Ngoài ra các kiểu như array, date thực chất cũng là object.

Typeof operator

Dùng toán tử typeof để xem kiểu của đối tượng.

Undefined value

Biến khi chưa được gán giá trị thì mang value là undefined, và typeof cũng là undefined.

Empty value

Các value rỗng như 0, chuỗi rỗng “”, null vẫn giữ được kiểu dữ liệu của nó. Ví dụ như sau.

Function type

Các hàm (function), phương thức (method) cũng là kiểu function, và được xem như các biến. Cả kiểu function và object thuộc loại kiểu phức (complex type).

2. Type casting

Chuyển đổi kiểu, hay còn gọi là ép kiểu (type casting) là chuyển từ một kiểu này sang kiểu khác. Trong JS, phần nhiều trường hợp sẽ được tự động chuyển (ngầm định – implicit), nhưng bạn cũng có thể tự tay ép kiểu theo cách thủ công.

Cú pháp mặc định cho type casting như sau.

Ví dụ.

Về cơ bản là vậy, bên cạnh đó các kiểu cụ thể sẽ có những method chi tiết hơn để thực hiện chuyển đổi. Ví dụ như từ number sang string thì có những method như toString(), toFixed(), toPrecision() chuyển kiểu nhưng với chức năng khác nhau.

Một vài lưu ý:

  • Khi xuất biến ra console, hoặc element,… thì JS tự động chuyển thành một string.
  • Trong các biểu thức (expression) tính toán thì JS chuyển kiểu theo một số quy tắc nhất định.

Nói chung nên hạn chế type casting, và khi cần thiết nên tránh để JS tự động thực hiện. Hạn chế việc thao tác khác kiểu dữ liệu, vì JS có thể tự động ép kiểu và cho ra kết quả không như mong đợi.

D. Basic commands

1. Conditional statements

If else statement

Lệnh if else trong JS khá giống với các ngôn ngữ khác nên mình không bàn nhiều ở đây.

condition là một biểu thức boolean, có được từ phép so sánh hoặc lấy trực tiếp từ biến, hàm. Nếu biểu thức đúng (true), lệnh đầu tiên sẽ được thực hiện, ngược lại thực hiện lệnh thứ 2 (nếu có).

Đặc biệt, câu lệnh trước else trong JS không cần chấm phẩy.

Các cặp if else có thể lồng nhau (nested) như sau, chú ý else if là hai từ riêng biệt.

Ternary operator

JS hỗ trợ toán tử ba ngôi (ternary operator) như sau.

Toán tử trả về kết quả là một trong hai value đã cho, dựa theo điều kiện condition. Nếu điều kiện đúng trả về true_value, ngược lại trả về false_value.

Switch case

Dùng so sánh một biểu thức với nhiều nhóm giá trị khác nhau, mỗi giá trị là một case. Nếu khớp với case nào, thì lệnh trong case sẽ được thực hiện. Còn không có case nào khớp thì default sẽ được gọi (nếu có, vì default có thể bỏ qua)

Sau mỗi case nên có break để ngắt, nếu không sẽ bị trôi case, tương tự các ngôn ngữ khác.

Chú ý, switch case sử dụng strict comparison (dấu ===) để so sánh, vì thế nên value của case và expresssion phải giống nhau.

2. Loop statements

While loop

Lặp lại một công việc với số lần xác định. Nếu điều kiện condition đúng thì lặp lại lần nữa, nếu không thì thoát.

Điều kiện dừng cần phải khả thi, để tránh vòng lặp chạy vô hạn sẽ gây crash trình duyệt.

Do while loop

Tương tự while loop, nhưng thực hiện ít nhất 1 lần, thực hiện xong lệnh mới kiểm tra điều kiện.

For loop

Tương tự trong ngôn ngữ khác, nên mình không bàn nhiều ở đây. Nhưng điểm khác biệt là ngôn ngữ khác dùng chấm phẩy để phân tách các phần bên trong for, thì JS sử dụng dấu phẩy (comma).

Ngoài ra, các statement bên trong có thể được bỏ qua (omitting).

Other loop

Hai vòng lặp for nữa trong JS là for of và for in. Ở đây mình ghi ra để làm quen với cú pháp, các bạn có thể bỏ qua, các chương sau sẽ có nhắc tới.

For of dùng để duyệt qua lần lượt các element (e) trong một iterable (mảng, chuỗi, map,…)

For in dùng lặp qua các thuộc tính của object.

Break & continue

Lệnh break dùng để ngắt một vòng lặp, còn continue để dừng lần lặp hiện tại và đi tiếp vòng lặp tiếp theo, tương tự các ngôn ngữ khác.

Ngoải ra break còn dùng cho label để break một label block.

E. Advanced concepts

1. Scope

Scope được hiểu là phạm vi hoạt động của một đối tượng trong JS, đối tượng chỉ hoạt động trong scope của nó. Ví dụ như một biến được khai báo bên trong một function, thì scope của nó là function đó. Đối tượng bị xóa khi chương trình chạy ra khỏi scope của nó.

Scope phụ thuộc vào vị trí khai báo và từ khóa dùng khai báo, nhưng chung quy lại đến nay có 3 loại scope:

  • Global scope: Có giá trị sử dụng trong toàn bộ chương trình
  • Function scope: Có phạm vi trong một function
  • Block scope (ES6): Phạm vi trong một khối (block), là phần code nằm trong cặp {}

Khai báo với var chỉ có hai loại scope là global scope và function scope. ES6 sử dụng let hoặc const, cho phép đối tượng có thể có block scope (tất nhiên let hoặc const vẫn có thể sở hữu global hoặc function scope).

Lưu ý object và function cũng là các biến, do đó chúng cũng có scope của riêng mình.

Global scope

Các đối tượng, biến, hằng, hàm, object,… được khai báo bên ngoài tất cả function, block thì có global scope. Lúc này chỉ khi đóng tab trình duyệt thì đối tượng global mới bị hủy.

Chú ý, nếu gán một biến mà không có từ khóa khai báo (const, let, var) thì nó sẽ trở thành global variable (biến toàn cục) và mặc định có global scope, dù cho nó được khai báo ở đâu.

Trong strict mode, hành vi này bị cấm.

Function scope

Các đối tượng được khai báo bên trong hàm, và không thuộc block scope thì có function scope.

Các tham số của hàm cũng được tính là function scope.

Block scope

Hai từ khóa được bổ sung trong ES6 là let và const cho phép một đối tượng khai báo có thể có block scope.

Block scope là phạm vi chỉ trong một block, là nằm trong khối {}. Trong một số trường hợp, như vòng lặp for ở ví dụ trên, thì biến let i cũng được tính vào trong block.

Ta nói let và const trong một khối {}, sẽ bị chặn bởi khối đó và có block scope. Còn var không bị {} chặn, nên nó vẫn sẽ giữ được scope trước đó.

Hành vi của block scope là giống với những ngôn ngữ khác, do đó nó được khuyến khích sử dụng. Đó là lí do nên dùng const hay let thay cho var khi khai báo.

2. Hoisting

Trong code đôi khi bạn sẽ gặp trình trạng “sử dụng trước, khai báo sau” như sau.

Được như vậy là nhờ có tính năng gọi là hoisting. Khi một đối tượng được hoisting, thì khai báo của nó được tự động mang lên đầu khối, trước các lệnh khác. Do đó mới có thể “sử dụng trước, khai báo sau”.

Những đối tượng được hoisting

Một số đối tượng chính bản thân nó đã được hoisting, một số thì cần được khai báo với var mới được hoisting.

Function được tự động hoisting.

Các đối tượng khai báo bằng var luôn được hoisting, trong khi khai báo với let hoặc const thì không.

Initial value is not hoisted

Khi sử dụng var để làm một biến được hoisting, thì chỉ có phần khai báo (declaration) được kéo lên trên, phần khởi tạo giá trị cho biến không được nâng lên theo. Do đó, khi sử dụng biến sẽ như chưa khởi tạo, tức là có giá trị undefined cho tới câu lệnh khai báo thực sự.

Ví dụ như sau.

Trong lệnh 1, thì có thể dùng x mà không bị báo lỗi. Nhưng giá trị của x lúc này không phải 5 mà là undefined, do số 5 không được kéo lên theo. Chỉ khi chạy tiếp tới lệnh var x = 5 thì x lúc này mới được gán số 5.

Đoạn code trên nên được viết lại như sau, sẽ giúp bạn dễ hiểu hơn.

Có nên dùng hoisting

Nên hạn chế dùng hoisting, và luôn khai báo trước mọi đối tượng khi sử dụng. Lại thêm một lý do nữa không nên dùng var để khai báo. Nếu bạn không hiểu rõ về hoisting, nó có thể gây lỗi chương trình và có những hành vi không mong muốn (như không hoist được giá trị khởi tạo).

3. Strict mode

Strict mode là một chế độ cho phép bạn viết code nghiêm ngặt (strict) và an toàn hơn, nhờ vào việc thêm một số quy tắc mà bạn không được phép làm (vì sẽ làm giảm tính an toàn, bảo mật của code). Strict mode giúp code được thực hiện chặt chẽ hơn, hạn chế lỗi và khai báo không an toàn.

Mặc định thì strict mode bị tắt, nhưng chúng ta có thể dùng dòng sau để bật lên.

Nhiều trình duyệt không hỗ trợ strict mode sẽ không hiểu được, nên không bật strict mode. Nhưng cũng không gây ra lỗi gì vì bản chất dòng lệnh trên chỉ là một chuỗi thông thường.

Tại sao nên dùng strict mode

Strict mode giúp bạn viết mã an toàn hơn. Trong chế độ bình thuòng, nếu bạn viết cú pháp không chuẩn thì đôi khi sẽ được bỏ qua, hoặc tự động xử lý. Việc xử lý tự động của JS đôi khi tạo ra một số hành vi không đúng với mong muốn, do đó gây ra lỗi và làm code kém an toàn.

Do đó, nên sử dụng strict mode để viết code được tốt hơn. Bên dưới là các quy tắc được strict mode quy định.

Không cho phép with và delete

Từ khóa with bị cấm vì lý do hiệu suất.

Delete bị cấm do việc xóa bỏ một đối tượng là không được phép, và có thể gây lỗi.

Không cho phép sử dụng biến chưa khai báo

Nếu sử dụng biến chưa được khai báo như sau.

Thì biến đó bình thường sẽ trở thành global variable, nhưng trong strict mode bị cấm. Biến phải được khai báo rõ ràng, với var, let hoặc const.

Không cho phép trùng tham số, tên thuộc tính

Khi khai báo function, strict mode không cho phép có hai tham số trùng nhau.

Đối với các thuộc tính của object cũng vậy, không thể có hai thuộc tính trùng tên.

Không cho phép hệ octal với chữ số 0 ở đầu

Việc khai báo sau là sai.

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo