Javascript - the bad part (continue)

Trong bài viết lần trước, chúng ta đã được thấy 1 số "bad part" trong javacript, phần này chúng ta sẽ tiếp tục tìm hiểu về những điểm khác mà cũng gây cho developer không ít phiền toái khi lập trình với javascritp

NaN

NaN là 1 giá trị đặc biệt trong javascript, nó có nghĩa là not a number mặc dù khi thực hiện phép toán để tìm kiểu của nó thì lại trả về number

typeof(NaN) === 'number'
=> true

Chúng ta có thể thu được giá trị NaN khi chuyển đổi 1 string không biểu diến 1 số về dạng số

+'123'
=> 123
+'123abc'
=> NaN

1 chuỗi các phép toán số học mà chứa toán tử NaN thì sẽ trả về NaN

1+2*3-NaN
=> NaN

Do phép toán typeof không thể dùng để phân biệt số thường và NaN nên 1 điều ngạc nhiên đã xảy ra là NaN không bằng chính nó (yaoming)

NaN === NaN
=> false
NaN !== NaN
=> true

Vì vậy javascript cung cấp hàm isNaN để phân biệt NaN với các giá trị số khác

isNaN(NaN)
=> true
isNaN(0)
=> false
isNaN('oops')
=> true
isNaN('0')
=> false

Phép toán tốt nhất để xác định 1 giá trị có phải là số hay không có lẽ là isFinite vì phép toán này sẽ loại bỏ giá trị NaNInfinity tuy nhiên phép toán này lại tự ép kiểu đối số

isFinite(1)
=> true
isFinite(NaN)
=> false
isFinite(1/0)
=> false
isFinite("1")
=> true

Nếu muốn xác định chính xác 1 giá trị có phải là số hay không bạn có thể tự định nghĩa 1 hàm isNumber như sau

var isNumber = function isNumber(value) {
  return typeof value === 'number' && isFinite(value);
}

Block-less Statements

Javascript cho phép viết các câu lệnh bên trong if, while block bằng cách tab lùi đầu dòng mà không cần dấu ngoặc nhọn {}. Điều đó có nghĩa là chúng ta có thể viết câu lệnh if dưới đây theo 1 cách khác

a = 1;
if (true) {
  a = 2;
}
a;
=> 2
a = 1;
if (true)
  a = 2;
a;
=> 2

Tuy nhiên cách viết này chỉ khả dụng khi chỉ có 1 câu lệnh đơn nằm trong câu lệnh if. Khi có nhiều hơn 1 câu lệnh, cách viết tưởng chừng trông đẹp hơn nhờ tiết kiệm được 2 dấu ngoặc nhọn này lại có thể gây ra nhầm lẫn tai hại

a = 1;
b = 2;
if (false) {
  a = 3;
  b = 4;
}
[a, b]
=> [1, 2]
a = 1;
b = 2;
if (false)
  a = 3;
  b = 4;
[a, b]
=> [1, 4]

Rõ ràng kết quả của 2 cách viết trên là khác nhau và trên thực tế cách viết thứ 2 sẽ tương đương với cách viết sau đây:

a = 1;
b = 2;
if (false) {
  a = 3;
}
b = 4;
[a, b]
=> [1, 4]

Vì vậy để đảm bảo không bị nhầm lẫn, tốt nhất là nên viết đầy đủ dấu ngoặc =))

Array

Javascript không có mảng thực sự. Chúng ta có thể khai báo 1 mảng trong javascript mà không cần khai báo kích cỡ của nó. Điều này thuận tiện cho việc khai báo và chúng ta cũng không cần lo lắng về lỗi tràn mảng khi truy cập 1 phần tử có index vượt quá kích cỡ mảng. Tuy nhiên do không có mảng thực sự nên hiệu năng của nó sẽ kém đi.

Việc xác định 1 đối tượng có phải là mảng hay không cũng khá rắc rối khi câu lệnh typeof lại trả về "object" thay vì "array".

a = [1, 2];
typeof(a);
=> "object"

Để xác định 1 đối tượng có phải là mảng hay không ta phải xem xét đến constructor của đối tượng đó

a = [1, 2];
if (a && typeof a === 'object' && a.constructor === Array) {
  "a is an array!"
}
=> "a is an array!"