Làm sao để tự tin về JS? Hãy nắm vững những điều này!

Khi tôi mới bắt đầu sử dụng JavaScript trong các dự án, tôi cảm thấy mình là một kẻ mù đang mò mẫm từng bước. Mặc dù tôi có thể hoàn thành được một trang web bằng JS Framework, thật khó để có thể hiểu rõ mình đang làm gì, có thiếu gì hay có cách nào khác tốt hơn để thực hiện một chức năng không? Tôi cũng đã tham gia nhiều cuộc phỏng vấn tìm việc nhưng có nhiều câu hỏi rất căn bản tôi vẫn không trả lời được. Bạn có thấy những điều này rất quen thuộc không? Và có cách nào để có một nền tảng kiến thức về JS vững vàng giúp bạn tự tin trong công việc? Hãy đọc tiếp nhé!

JS thực sự là một ngôn ngữ khó hiểu, rất dễ để bắt đầu nhưng cũng dễ nhầm lẫn. Sau nhiều năm tháng chinh chiến và chìm đắm trong ngôn ngữ vô cùng phổ biến này, tôi đã rút ra những kiến thức cô đọng làm nền tảng để học hỏi bất kỳ Framework nào, đó là những gạch đầu dòng sau đây:

  • Value:
    • Type of value: number, string, object, null, undefined, function. Dùng typeof <value> để kiểm tra type của nó
    • Primitive values: number, string. Đây là những value mà bạn không thể thêm hay sửa đổi, ví dụ mỗi khi viết let a = 2 bạn có giá trị 2, nhưng bạn không thể tạo thêm một “2” nào khác, hoặc làm cho 2 trở thành 3. Điều này cũng đúng với string
    • null và undefined. Đây là 2 giá trị đặc biệt vì có nhiều thứ bạn không làm được với chúng. null biễu diễn việc thiếu vắng giá trị một cách có chủ đích, trong khi undefined thì không chủ đích.
  • Equality: hai giá trị bằng nhau khi chúng có cùng giá trị, đơn giản phải không? Thực tế trong JS có 2 cách so sánh là strict equality dùng 3 dấu “=”, trong khi loose equality sẽ tự động ép kiểu biến trước khi so sánh, vì vậy mà ta có “2” == 2 return true. Cần tránh sử dụng loose equality khi có thể!
  • Literal: là khi bạn dùng giá trị trực tiếp, ví dụ 2 hay “banana”
  • Variable: trái ngược với literal, dùng variable để trỏ tới một giá trị thông qua tên biến. Nhờ vậy thay vì viết giá trị 2 mọi nơi, có thể gán biến a = 2 rồi dùng a để biểu diễn giá trị này.
    • scope: phạm vi mà variable có hiệu lực (global, function hay block scope)
    • assignment: ví dụ gán a = 2
    • let vs const vs var: bởi vì scope của var khá khó hiểu, trong các phiên bản mới của JS, chúng ta đều có thể dùng let và const, trong đó const chỉ một biến mà chỉ được phép assign một lần duy nhất.
  • Object: object thực ra cũng là một giá trị đặc biệt trong JS, nó đặc biệt ở chỗ có thể trỏ tới các giá trị khác.
    • Property: là một phần của object dùng để trỏ tới các giá trị khác nằm ngoài object.
    • Object literal: khai báo một object bằng cách viết nó vào program ví dụ {}, {name: ‘Tom’}
    • Object identity: mỗi khi ta viết “2” ta đều chỉ tới một giá trị duy nhất, tuy nhiên khi viết “{}” thì ta lại có một giá trị khác nhau! Nếu không tin bạn có thể thử so sánh {} === {} sẽ cho ra kết quả là false. Tại sao vậy? vì khi so sánh 2 giá trị là chúng ta đang kiểm tra xem chúng có cùng trỏ tới một giá trị không, và mỗi lần viết {} JS lại tạo ra một object nên kết quả là không bằng nhau.
    • Dot Notation: dùng để đọc/ghi một property của một object, ví dụ person.name = ‘Tom’;
    • Bracket Notation: khi không biết tên của property, hoặc nó là giá trị động, thì ta có thể dùng ngoặc vuông như sau person[part], {[part]: “my body part”}
    • Mutation: nói tới khả năng một object có thể thay đổi giá trị của property mà nó trỏ tới. Lưu ý là sử dụng const thì object vẫn có thể thay đổi giá trị trỏ của property.
    • Array: một dạng dữ liệu rất phổ biến và hữu ích. Nó có các hàm built-in như: map, filter, reduce, join
    • Prototype: giả sử khi đọc một property của object mà nó không tồn tại thì sao? Bạn có thể nghĩ ngay là nó return giá trị undefined, tuy nhiên chính xác hơn nó sẽ tìm kiếm property này trong prototype của object. Có thể coi prototype là một property ẩn của mỗi object. Prototype trỏ tới một object, và object này lại có Prototype, nên việc tìm kiếm sẽ tiếp tục qua Prototype chain như vậy cho đến khi tận cùng của Prototype chain. Ví dụ mỗi object thì sẽ có root của Prototype chain là Object với prototype trỏ tới null. Bạn có thể ít khi để ý đến prototype tuy nhiên chúng ta rất thường xuyên dùng method toString() là một method được định nghĩa sẵn trong prototype.
  • Function: Hàm cũng là một giá trị đặc biệt, nó biễu diễn một đoạn code trong chương trình.
    • Arguments (hay Parameters) là các tham số đầu vào của hàm
    • Function Expression: có khai báo hàm giống biến let sayHi = function() {}
    • Function Declaration: function sayHi() {}
    • Function Hoisting: khai báo hàm được tự động đẩy lên đầu scope mà nó khai báo, nhờ vậy có thể gọi hàm ở bên trên vị trí hàm được khai báo.
    • this: khái niệm hay bị nhầm lẫn nhất trong JS. this giống với tham số của hàm, nó được tự động gán giá trị tuỳ thuộc vào nơi nó được gọi, chứ không phải nơi hàm được khai báo. Vì vậy mới sinh ra các hàm bind, call, apply để dễ dàng quản lý giá trị của this.
    • Arrow function: khá giống Function Expression, có thể khai báo hàm một cách ngắn gọn hơn như sau let sayHi = () => {}. Chú ý là arrow function không có chứa this, mà this trong hàm sẽ trỏ tới hàm gần nhất trong phạm vi scope của nó.
    • Function Binding: ví dụ có thể gán một hàm f cho một giá trị this bằng method bind()
    • Call Stack: mỗi khi hàm được gọi thì các giá trị trong hàm sẽ bị reset lại, sau khi hàm chạy xong thì nó cũng biến mất cùng các biến ở bên trong, và chúng ta trở lại với hàm đã gọi nó. Cứ như vậy chúng ta có một Call Stack là một stack chứa các hàm sẽ được execute.
    • Recursion: hàm có thể gọi chính nó, cần chú ý có thể xảy ra trường hợp hàm không bao giờ chạy xong vì thiếu điều kiện kết thúc. Khi đó xảy ra lỗi stack overflow vì Call Stack có quá nhiều lời gọi hàm trồng lên nhau.
    • Higher-Order Function: là hàm trong đó tham số là một số hàm khác hoặc return ra các hàm khác. Ví dụ các hàm built-in của Array (map, filter, reduce) là Higer-Order Function
    • Callback: thực ra callback không phải là một khái niệm trong JS, mà nó là một pattern. Bạn đưa một function làm tham số cho một function khác, và muốn nó được gọi sau. Ví dụ setTimeout là nhận đầu vào là một function và thực thi nó sau khi kết thúc thời gian timeout. Lưu ý là callback function cũng là function bình thường, chỉ khác thời điểm gọi.
    • Closure: bình thường khi function kết thúc thì tất cả các biến của nó cũng biến mất theo. Tuy nhiên với trường hợp khai báo function trong một function khác thì function ở trong có thể được thực thi sau, và nó có thể đọc được các biến của function ngay bên ngoài nó. Điều này có được là nhờ JS đã lưu lại các biến của outer function. Trong thực tế việc này rất hữu ích, bạn có thể dùng closure rất nhiều mà không để ý đến nó.

Trên đây là những khái niệm cơ bản nhưng cũng quan trọng nhất của JS. Khi hiểu được những gạch đầu dòng này tôi tin chắc là bạn đã sẵn sàng tiếp thu những kiến thức khác hay JS Framework khác một cách dễ dàng.

Leave a comment