Type checking trong JavaScript

MỞ ĐẦU

Javascript là ngôn ngữ không cần chỉ rõ kiểu dữ liệu khi khai báo biến. Nếu phải định rõ kiểu cho biến sẽ làm giảm phần lớn sức mạnh của Js, tuy nhiên trong nhiều trường hợp, việc hiểu rõ các cách kiểm tra kiểu dữ liệu của biến vẫn rất cần thiết. Có nhiều cách để kiểm tra kiểu dữ liệu trong JavaScript, tuy nhiên trong bài viết này, mình xin giới thiệu 4 cách: toán tử typeof, toán tử instanceof, constructor property và phương thức toString của Object.

1. typeof operator

Toán tử typeof trong JavaScript trả về một chuỗi mô tả kiểu dữ liệu của bất kỳ toán hạng đầu vào nào. Trong ECMAScript 5, có 6 giá trị có thể được trả về khi thực hiện type checking:

typeof 'str';        // "string"
typeof 10;           // "number"
typeof true;         // "boolean"
typeof {};           // "object"
typeof function(){}; // "function"
typeof undefined;    // "undefined"

Tính nhất quán và độ tin cậy của toán tử này là không cao, ta có thể gặp phải vấn đề khi sử dụng toán tử này với các kiểu dữ liệu không phải là một trong những kiểu bên trên, ví dụ như là mảng, null các loại custom Object. Hơn nữa, type checking theo cách này có rùi ro do sẽ phải so sánh chuỗi (case sensitive versuse insensive hoặc lỗi chính tả).

typeof null;                     // "object"
typeof [];                       // "object"
typeof new String('str');        // "object"
typeof new function Custom(){};  // "object"

typeof {} === 'Object';          // false
typeof {} === 'objcet';          // false

2. instanceof operator

Toán tử instanceof so sánh hai giá trị (một biến và một function) bằng cách kiểm tra chuỗi nguyên mẫu (prototype chain) của biến với nguyên mẫu của function object. Vì toán hạng thứ 2 có thể là bất cứ function nào mà chings ta muốn, nên instanceof có thể được sử dụng để kiểm tra các custom types nữa. Hơn nữa, bởi instanceof sử dụng toán hạng là một function thay vì một chuỗi, do đó, nếu ta có vô tình gõ nhầm thì trình biên dịch sẽ thông báo lỗi ngay.

{} instanceof Object;               // true
[] instanceof Array;                // true
(function(){}) instanceof Function; // true

var Foo = function(){};
new Foo() instanceof Foo;           // true

new Foo() instanceof Doo;           // ReferenceError: Doo is not defined

Kiểm tra chuỗi nguyên mẫu của đối tượng thì khá hữu ích, tuy nhiên instanceof sẽ luôn luôn trả về false cho các kiểu dữ liệu nguyên thủy (chuỗi, number, booleans, null và undefined) bởi chúng không phải là đối tượng và do đó không có nguyên mẫu.

'str' instanceof String;        // false
9 instanceof Number;            // false
true instanceof Boolean;        // false
null instanceof Object;         // false
undefined instanceof Object;    // false

Mặc dù chúng ta null hoặc undefined không thể là toán hạng thứ 2 của toán tử instanceof (vì chúng không phải là function), ta vẫn có thể dễ dàng kiểm tra null và undefined với toán tử ===.

undefined instanceof undefined; // TypeError: Expecting a function in instanceof check, but got undefined
null instanceof null;           // TypeError: Expecting a function in instanceof check, but got null

undefined === undefined;        // true
null === null;                  // true

3. constructor property

Các đối tượng kế thừa constructor property từ nguyên mẫu của chúng, chúng ta có thể sử dụng nó để kiểm tra kiểu của biến mà không phải kiểm tra toàn bộ chuỗi nguyên mẫu. Giống như instanceof, ta đang kiểm tra function object, vì vậy chúng ta có thể kiểm tra custom function.

({}).constructor === Object;           // true
/regex/.constructor === RegExp;        // true
(new Date()).constructor === Date;     // true

[].constructor === Array;              // true
[] instanceof Array;                   // true
[].constructor === Object;             // false
[] instanceof Object;                  // true

var Custom = function(){};
(new Custom()).constructor === Custom; // true

Cách này cũng áp dụng với các kiểu dữ liệu nguyên thủy, bởi JavaScript sẽ wrap các giá trị này vào trong các class Number, String và Boolean khi chúng ta cố gắng truy cập vào một thuộc tính trên chúng.

(9).constructor === Number;   // true
'str'.constructor === String; // true
true.constructor === Boolean; // true

Tuy nhiên, vì cách này truy cập trực tiếp vào property thay vì thông qua method, nên có thể gặp tình huống vô tình sửa đổi giá trị của property

var Custom = function(){};
var custom = new Custom();

custom.constructor = null;
custom.constructor === Custom; // false

custom = null;
custom.constructor === Custom; // TypeError: Cannot read property 'constructor' of null
custom === null;               // true

4. Object.prototype.toString method

Cách này thì khắc phục được nhiều vấn đề của các phương pháp bên trên. Object.prototype.toString sẽ trả về một chuỗi theo form "[object Type]", và nó có thể được so sánh tương tự như cách dùng typeof. Không may là ta vẫn phải so sánh chuỗi, và các custom object sẽ trả về chuỗi "[object Object]".

[1,2,3].toString();                        // "1,2,3"
Object.prototype.toString.call([1,2,3]);   // "[object Array]"
Object.prototype.toString.call(new Date())
  .slice(8, -1) === 'Date';                // true
Object.prototype.toString.call(
  new (function Custom(){}));              // "[object Object]"

Giống như constructor, method này sẽ wrap các kiểu dữ liệu nguyên thủy vào các đối tượng tương ứng của chúng. Thêm nữa, phương pháp này còn áp dụng được cho cả trường hợp null hoặc undefined.

Object.prototype.toString.call('str');     // "[object String]"
Object.prototype.toString.call(10);        // "[object Number]"
Object.prototype.toString.call(false);     // "[object Boolean]"
Object.prototype.toString.call(null);      // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"

KẾT LUẬN

I would be presumptuous to claim there is one best way to check types in JavaScript. In truth, there are many valid ways, each with their pros and cons, and each better suited for specific situations and projects. Having a better understanding of these options gives us power to make an informed decision that’s best for our code. For convenience, here is a table summarizing a few properties of each approach. Không thể khẳng định đâu là cách tốt nhất để kiểm tra kiểu dữ liệu trong JavaScript. Trong thực tế, có rất nhiều cách, mỗi cách đều có ưu và khuyết điểm riêng, và phù hợp hơn cho từng tình huống cụ thể của dự án. Nắm rõ càng nhiều phương pháp, ta lại càng có thể lựa chọn phương pháp phù hợp nhất với mình. Để thuận tiện, dưới đây là một bảng tóm tắt một vài tính chất của mỗi cách trên

typeof instanceof constructor tostring
Tránh phải so sánh chuỗi O O
commonly used O O
checks custom classes O O
check null trực tiếp O
check undefined trực tiếp O O
hoạt động trong trường hợp window khác nhau (1) O O

Chú thích

(1) Mỗi window / frame lại tạo instance unique cho mỗi đối tượng build-in. Vì vậy nếu chúng ta so sánh Array window này với Array ở window khác, 2 giá trị này sẽ không bằng nhau.

Nguồn tham khảo

  1. http://engblog.yext.com/post/js-type-checking