Have you ever wondered: what is the correct way to check if a Javascript variable is an Array?
Do a Google search and you will get a great variety of answers. And, unfortunately, there's is no correct answer. This is one of the sad things about Javascript, not only are there many varying implementations of the language, there are also many varying opinions about how things should be done.
Enough philosophizing and feeling sorry about the state of things. With this article, I will trying to give a comprehensive overview of the different techniques of checking-types in Javascript, the pros and cons of each and why they exist.
typeof operator
In the beginning, there was typeof. This handy operator gives you the "type" of a Javascript value:
typeof 3 // "number" typeof "abc" // "string" typeof {} // "object" typeof true // "boolean" typeof undefined // "undefined" typeof function(){} // "function"
All is fine 'n dandy until
typeof [] // "object"
Huh? An array's type is object? I guess it is, if you want to get technical about it, but still, what the...
typeof null // "object"
Okay, now that's just wrong!
Also, typeof will return "object" for Dates, RegExp, user defined objects, DOM elements, and pretty much anything else. So, typeof is pretty good at distinguishing between different kind of primitive values, and distinguish between them and objects, but is completely useless when it comes to distinguishing between different kinds of objects - which includes arrays and nulls(WTF?!).
instanceof operator
The instanceof operator tells you whether a object is an instance of a certain type. The so-called "type" is a constructor. For example
function Animal(){}
var a = new Animal()
a instanceof Animal // true
Alternatively, you could make this check using the constructor property of an object
a.constructor === Animal // true
However, the constructor check has two problems. First, it does not walk up the prototype chain
function Cat(){}
Cat.prototype = new Animal
Cat.prototype.constructor = Cat
var felix = new Cat
felix instanceof Cat // true
felix instanceof Animal // true
felix.constructor === Cat // true
felix.constructor === Animal // false
The second problem is that it will bomb out if the object in question is null or undefined.
felix = null
felix instanceof Animal // true
felix.constructor === Animal // throws TypeError
[1, 2, 3] instanceof Array // true
/abc/ instanceof RegExp // true
({}) instanceof Object // true
(function(){}) instanceof Function // true
or does it?
3 instanceof Number // false
true instanceof Boolean // false
'abc' instanceof String // false
Okay, what is going on here? It turns out that instanceof does not work with primitive values. The primitive types in Javascript are: strings, numbers, booleans, null, and undefined - (Oh good! You can count them all on one hand!) Well actually, I should have said it does not work with primitives with the exception of null and undefined, because they are not an instance of anything, and so instanceof correctly returns false when either is used on the left hand side.
null instanceof Boolean // false
undefined instanceof Array // false
To top that off though, checking for the constructor property will work for the primitive types number, string and boolean.
(3).constructor === Number // true
true.constructor === Boolean // true
'abc'.constructor === String // true
This works because whenever you reference a property on a primitive value, Javascript will automatically wrap the value with an object wrapper, like so
var wrapper = new Number(3)
except you don't see this - it happens behind the scenes. The wrapper then will be an instance of - in this case Number - or a Boolean or a String depending on the type of the primitive value, at which point it can walk up the prototype-chain and get at the properties of the Number prototype, etc. So, for example, creating a wrapper manually will make the instanceof operator work
new Number(3) instanceof Number // true
new Boolean(true) instanceof Boolean // true
new String('abc') instanceof String // true
But doing that would be pointless because it requires you to already know the type of the value of which you are asking whether or not it is of the type that you already know it is.
Cross-window Issues of instanceof
It turns out that instanceof has another problem. It breaks down when you try to test an object coming from another window. You know? The ones that are created for each <iframe>, <frame> or popup window that you create.
<html> <body> </body> <script> var iframe = document.createElement('iframe') document.body.appendChild(iframe) var iWindow = iframe.contentWindow // get a reference to the window object of the iframe iWindow.document.write("<script>var arr = [1,2, 3]<\/script>") // create an array var in iframe's window console.log(iWindow.arr) // [1, 2, 3] console.log(iWindow.arr instanceof Array) // false </script> </html>
Above, we created a variable arr inside the context of the iframe and set it to the array [1, 2, 3]. However, we get false when we ask whether it is a instance of Array. What is happening?!! Behold.
Array === iWindow.Array // false
The Array in the iframe is not the same Array as our Array! This is true for all built-in objects: there are two versions of all of them! Basically, we have parallel universes! OMG!
What this means is that an array created within the iframe is only an instance of the Array constructor within the iframe
iWindow.arr instanceof iWindow.Array // true
The same phenomenon happens with windows created using the open() function. It is because of this cross-window problem that the use of instanceof is somewhat limited and discouraged within the Javascript community.
Duck-Typing
Because neither typeof or instanceof are satisfactory, many resort to duck-typing. This means checking the behavior: if it looks like a duck and quacks like a duck, then it is a duck as far as I am concerned. Pretty sure I misquoted that...oh well.
So, using duck-typing, an isArray check might look like
// source: http://forums.devshed.com/javascript-development-115/javascript-test-whether-a-variable-is-array-or-not-33051.html function isArray(obj){ return (typeof(obj.length)=="undefined") ? false:true; }
or
// source: http://www.hunlock.com/blogs/Ten_Javascript_Tools_Everyone_Should_Have function isArray(testObject) { return testObject && !(testObject.propertyIsEnumerable('length')) && typeof testObject === 'object' && typeof testObject.length === 'number'; }
in jQuery, the function to check whether an object is a window is
isWindow: function( obj ) { return obj && typeof obj === "object" && "setInterval" in obj; }
You could easily trick this function into returning true
$.isWindow({setInterval: 'bah!'}) // true
Clearly, the problem with this approach is that
- it is inexact and can have false positives
- the set of properties of the object to test is arbitrary, and so it's unlikely for different people to agree on one way of doing it
Object.prototype.toString method
It turns out that, you can get type information about an object by using the Object.prototype.toString method.
Object.prototype.toString.call(3) // "[object Number]"
Object.prototype.toString.call([1, 2, 3]) // "[object Array]"
Object.prototype.toString.call({}) // "[object Object]"
This native method is rarely encountered normally because it's usually shadowed by another toString method lower down in the prototype chain(Array.prototype.toString, Number.prototype.toString, etc.). This method reliably differentiates between native types, however, will return "[object Object]" for all user-defined types
Object.prototype.toString.call(new Animal) // "[object Object]"
It does, however, work across different window contexts
Object.prototype.toString.call(iWindow.arr) === "[object Array]" // true
with one exception: different windows(as in popup window) in IE.
var pWindow = open("")
pWindow.document.write('<script>var arr = [1, 2, 3]</script>')
Object.prototype.toString.call(pWindow.arr) // you get "[object Object]" in IE; "[object Array]" else where.
This is strange because for iframes it works just fine, bummer! This method has become somewhat of a preferred way to differentiate native types despite the IE bug.Ehh, nobody uses popup windows anymore anyway.
Function.prototype.toString method
Yet another way to test for type information is by using the Function.prototype.toString method.
Function.prototype.toString.call((3).constructor)
// "function Number() {
// [native code]
// }"
The method gives you the complete source of a function. In the case of native functions, it just says "[native code]" in the body. One could easily parse out the name of the function to figure out type of the object using this helper function
function type(obj){
var text = Function.prototype.toString.call(obj.constructor)
return text.match(/function (.*)\(/)[1]
}
type("abc") // "String"
This one will work for user-defined types too
type(new Animal) // "Animal"
this code has a problem wrt popup windows in IE just like instanceof. It's because when Function.prototype.toString is called with a constructor from another parallel universe, it can only discern the constructor as an object("[object Object]"), and thus rejects the argument and throws a "Function expected" error. This problem can actually be worked around by referencing the toString method indirectly
function type(obj){
var text = obj.constructor.toString()
return text.match(/function (.*)\(/)[1]
}
Now, it works for popup windows in IE too! This fix makes it susceptible to shadowing
Array.toString = function(){ return "function NotArray(){ }" }
type([1,2,3]) // "NotArray"
but still, I'd say this is pretty cool.
Now, let's return to user-defined types for a minute. With this approach, there's no way to distinguish between two different user-defined types with the same name
var f = function Animal(){ "something" }
var g = function Animal(){ "something entirely different" }
type(new f) // "Animal"
type(new g) // "Animal"
For this reason, this method is inferior to instanceof for user-defined types. Another seemingly obvious problem with this approach is performance, but I'd have to benchmark it(jsperf!) to make a real claim.
DOM Elements and Host Objects
So far, I have not mentioned type checking for DOM elements and host objects. That's because it's hard. With the exception of duck-typing, none of the methods mentioned above will work for all browsers. If you drop IE7 and below, however, you can actually get some of the things to work. The output below were created using Tutti
> 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
First up, typeof is useless, that was expected. Next, everyone but IE 7 recognizes that a div is an instance of Element as well as an HTMLDivElement. In IE7, those constructors aren't even present. Next,
> 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]
The result of Object.prototype.toString in IE - even IE 8 - is particularly unhelpful. What about
> 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] }
Function.prototype.toString: it gives us something useful for IE8, but every browser has a slightly different output.
Fun! Try another one? Allllllllllllllllrrrighty then!
> 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] }
With window it is just all over the place, none of these methods worked for all browsers. You can try testing out some other host objects if you want, but in general it doesn't look doable. However, in my testing, the XMLHttpRequest object and DOM and Text elements look doable using instanceof, if you can drop support for IE7 and below.
What We've Learned
Checking types in Javascript is a big mess. Although it's really not that hard to check for any one particular type, it is definitely not consistent across types, and you probably have had to make a lot of choices along the way. So it helps to know about all the different options. In an upcoming post, I will try to make all of this easier with a small piece of code. Stay tuned.
相关推荐
#### Types in JavaScript JavaScript has nine distinct data types: 1. **Null** (`null`): Represents a null value. It throws an error if you try to access any property on `null`. It is never boxed. 2....
JavaScript Applications with Node.js, React, React Native and MongoDB: Design, code, test, deploy and manage in Amazon AWS By 作者: Eric Bush ISBN-10 书号: 0997196661 ISBN-13 书号: 9780997196665 出版...
Interview: Larry Wall—Part 2: Scripting Languages in General and Perl in Particular 388 History Note 396 History Note 397 History Note 401 9.6 Parameters That Are Subprogram Names 408 History...
In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types....
While Django provides many built-in field types, there may be cases where you need to store more complex data structures. Custom fields allow for greater flexibility in handling non-primitive data ...
Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking Java Virtual Machine Java Virtual Machine Support for Non-Java Languages: Java SE 7 introduces a new JVM ...
The json module: JavaScript Object Notation The plistlib module: A Property-List Parser ctypes Enhancements Improved SSL Support Deprecations and Removals Build and C API Changes Port-Specific ...
- **类型检测(Type Checking)**:`typeof`、`instanceof`、`Object.prototype.toString.call`等方法用于检测类型。 - **引用类型(Reference Types)**:对象是引用类型,通过引用传递,而非值传递。 - **null ...
Splitting a page when full is simple and does not require a tree traversal for node overflow checking as in a b+tree. Main page list updates are infrequent and hence the locking of the main page list...
5. 类型检测(Type Checking) - `typeof` 操作符:返回一个字符串,表示变量的数据类型,但注意它不能准确识别null和对象。 - `instanceof` 运算符:用于判断对象是否为某个构造函数的实例。 - `Object....
例子name : Required review checkon : pull_request_review : pull_request : types : [ opened, reopened, synchronize ]jobs : check : name : Checking required reviews runs-on : ubuntu-latest # GitHub ...
This will, of course, mean that if someone types the directory name into their browser, a full listing of all the files in that directory will be shown. This could be a security risk for your site....
此外,通过`Window -> Preferences -> General -> Content Types`,可以统一设定不同类型的文件编码,比如将JavaScript文件设置为UTF-8。 在开发JSP页面时,需要确保JSP文件的默认编码也是UTF-8。这可以通过`Window...
实时验证功能会检查XML、JSP、JSF、JavaScript等文件的语法错误,虽然有助于保持代码质量,但在大型项目中可能成为性能瓶颈。为了加速MyEclipse的响应速度,可以考虑禁用实时验证,只在需要时手动执行验证操作。具体...
- **位置**:`General -> Editors -> Text Editors -> Spelling`,取消勾选`Spell checking`。 **4. 暂停验证器** - **位置**:`General -> Validation`,勾选`Suspend all validators`。 **5. 定制视图** - ...
3. **类型检查与转换(Type Checking & Transformation)**:MPS 可以支持对 TypeScript 代码的静态类型检查,并将模型代码转换成可执行的JavaScript代码。 4. **代码生成(Code Generation)**:MPS 可以生成符合 ...
是什么? :hear-no-evil_monkey: 非常简单的小型JS类型检查功能。 它完全支持TypeScript! npm i is-what或可通过以下网址获取"deno.land/x/is_what" : "deno.land/x/is_what"动机我之所以建立它,是因为现有的解决...