Kiểm tra kiểu dữ liệu trong javascript

Kiểm tra kiểu dữ liệu trong javascript

Javascript là ngôn ngữ không ràng buộc về kiểu dữ liệu: không cần khai báo kiểu dữ liệu khi khai báo biến, một biến đang thuộc kiểu dữ liệu này có thể bị gán bởi một giá trị thuộc kiểu dữ liệu khác. Để xác định chính xác kiểu của một biến lúc runtime, nhiều khi trong một số hoàn cảnh cụ thể cũng không phải là một chuyện đơn giản.

Sử dụng toán tử typeof

Dùng typeof sẽ đúng với các trường hợp sau:

typeof 3 // "number"
typeof "abc" // "string"
typeof {} // "object"
typeof true // "boolean"
typeof undefined // "undefined"
typeof function(){} // "function"

Nhưng rất tiếc trong các trường hợp sau thì có vẻ không chuẩn:

typeof [] // "object"
typeof null // "object"
typeof new Date() // "object"
typeof /abc/ // "object"
typeof new RegExp("abc") // "object"
typeof new (function UserDefinedObject(){}) // "object"

typeof đều trả về kết quả là object nếu biến đó là kiểu Date, RegExp, HTML DOM, object do người dùng định nghĩa. Những trường hợp trên thì có thể chấp nhận được nhưng mảng mà đặc biệt là null mà lại cho kiểu là object thì khá vô lý.

Sử dụng toán tử instanceof

Dùng instanceof rất tốt với trường hợp biến không phải thuộc kiểu nguyên thủy:

function UserDefinedObject(){}
var a = new UserDefinedObject();
a instanceof UserDefinedObject // true
new Date() instanceof Date // true
/abc/ instanceof RegExp // true
new RegExp("abc") instanceof RegExp // true
[] instanceof Array // true

Rất tiếc instanceof lại không thực hiện được với các giá trị kiểu nguyên thủy như string, number, boolean, null và undefined.

3 instanceof Number // false
true instanceof Boolean // false
'abc' instanceof String // false

Trong trường hợp này cũng không sợ lắm vì ta có thể dùng typeof để xử lý.

Vấn đề của instanceof khi kiểm tra kiểu của biến tại cửa số khác

Việc sử dụng instanceof để kiểu tra kiểu dữ liệu của biến sẽ gặp vấn đề khi biến đó nằm ở khác cửa số (window) với ngữ cảnh đang kiểm tra. Ví dụ kiểm tra kiểu của một mảng nằm trên iframe, frame hoặc popup window chẳng hạn:

var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
// lấy về tham chiếu đến đối tượng window của iframe
var iWindow = iframe.contentWindow;
// tạo ra mạng arr trong iframe
iWindow.document.write('<script>var arr = [1, 2, 3]</script>');
iWindow.arr // [1, 2, 3]
iWindow.arr instanceof Array // false

Nguyên nhân của việc này là browser tạo cho mỗi một window một sandbox riêng, kiểu dữ liệu (class) Array trong window này hoàn toàn độc lập và khác biệt với kiểu (class) Array ở window khác.

Trong trường hợp trên, để kiểm tra ta làm như sau:

iWindow.arr instanceof iWindow.Array // true

Sử dụng thuộc tính constructor

Một biến x luôn có thể gọi x.constructor để nhận về tên hàm khởi tạo của nó (không áp dụng khi biến bằng null hoặc undefined)

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

(3).constructor === Number // true
true.constructor === Boolean // true
'abc'.constructor === String // true

Sử dụng phương thức Object.prototype.toString

Khi sử dụng Object.prototype.toString ta thu được như sau:

Object.prototype.toString.call(3) // "[object Number]"
Object.prototype.toString.call([1, 2, 3]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"

Cách này chỉ đúng với các kiểu dữ liệu built-in, đối với tất cả các class do người dùng tự định nghĩa thì đều trả về [object Object]

Object.prototype.toString.call(new Animal()) // "[object Object]"

Trong trường hợp khác ngữ cảnh window, dùng cách này là chính xác trừ duy nhất trường hợp popup window và chạy trên IE

var pWindow = open("")
pWindow.document.write('<script>var arr = [1, 2, 3]</script>')
Object.prototype.toString.call(pWindow.arr) // IE: "[object Object]" còn lại "[object Array]"

Sử dụng phương thức Function.prototype.toString

Cách sử dụng: Function.prototype.toString.call(x.constructor). Đối với các kiểu dữ liệu built-in sẽ ra dạng như sau:

Function.prototype.toString.call((3).constructor)
// "function Number() {
//    [native code]
// }"

Đối với các class do người dùng tự định nghĩa thì ta thu được chuỗi định nghĩa hàm khởi tạo.
Dựa vào đó ta lấy ra tên của kiểu dữ liệu (class):

function type(obj){
	var text = Function.prototype.toString.call(obj.constructor)
	return text.match(/function (.*)\(/)[1]
}

type("abc"); // "String"
type(3); // "Number"
type(new Animal()) // "Animal"

Với cách này dùng được cho trường hợp popup window với IE.

Kiểm tra kiểu dữ liệu cho DOM Element

Tôi sẽ sử dụng các cách thức ở trên để kiểm tra kiểu của biến DOM Element:

typeof và instanceof

> var div = document.createElement(‘div’)
> typeof div
Safari 5.0 => object
Firefox 3.6 => object
IE 7.0 => object
IE 8.0 => object
Opera 11.01 => object
> div instanceof Element
Safari 5.0 => true
Firefox 3.6 => true
IE 7.0 => Error: ‘Element’ is undefined
IE 8.0 => true
Opera 11.01 => true
> div instanceof HTMLDivElement
Safari 5.0 => true
Firefox 3.6 => true
IE 8.0 => true
IE 7.0 => Error: ‘HTMLDivElement’ is undefined
Opera 11.01 => true

 
Object.prototype.toString

> Object.prototype.toString.call(div)
Safari 5.0 => [object HTMLDivElement]
Firefox 3.6 => [object HTMLDivElement]
IE 7.0 => [object Object]
IE 8.0 => [object Object]
Opera 11.01 => [object HTMLDivElement]

 
constructor

> div.constructor.toString()
Safari 5.0 => [object HTMLDivElementConstructor]
Firefox 3.6 => [object HTMLDivElement]
IE 7.0 => Error: ‘div.constructor’ is null or not an object
IE 8.0 => [object HTMLDivElement]
Opera 11.01 => function HTMLDivElement() { [native code] }

 
Biến window

> typeof window
Safari 5.0 => object
Firefox 3.6 => object
IE 8.0 => object
IE 7.0 => object
Opera 11.01 => object
> window instanceof Window
Safari 5.0 => ReferenceError: Can’t find variable: Window
Firefox 3.6 => true
IE 8.0 => true
IE 7.0 => Error: ‘Window’ is undefined
Opera 11.01 => ReferenceError: Undefined variable: Window
> Object.prototype.toString.call(window)
Safari 5.0 => [object DOMWindow]
Firefox 3.6 => [object Object]
IE 8.0 => [object Object]
IE 7.0 => [object Object]
Opera 11.01 => [object Window]
> window.constructor
Safari 5.0 => function Object() {
[native code]
}
Firefox 3.6 => function Object() {
[native code]
}
IE 8.0 => [object Window]
IE 7.0 => undefined
Opera 11.01 => function Object() { [native code] }

Kết luận

Việc xác định kiểu của biến trong javascript là tương đối đơn giản trong các trường hợp phổ biến. Nhưng để xác định chính xác trong mọi trường hợp thì không phải là một chuyện dễ dàng. Chúng ta có rất nhiều cách để xác định và phán đoán kiểu dữ liệu và việc vận dụng nên linh hoạt trong các tình huống cụ thể.

Khi trích dẫn bài viết từ tek.eten.vn, xin vui lòng ghi rõ nguồn. Chúng tôi sẽ rất cảm ơn bạn!