This article was translated, original article here.
JavaScript Data Types
JavaScript is a loosely typed language, making type checking essential during variable usage. However, JavaScript doesn't strictly define primitive types. From a top-down perspective, there are only two categories: Primitive (primitive values, or primitive data types) and object. The distinction is simple: primitives lack properties and methods, while objects possess them. Primitives are immutable and represent the lowest-level data representation in JavaScript. Developers rarely interact directly with primitive values—when accessing them, JavaScript automatically wraps primitives in objects. For example, the string 'foo' is a primitive. When executing 'foo'.includes('f'), a String object is created to wrap 'foo'. The primitive itself has no methods/properties; it's actually accessing String.prototype.includes().
There are 7 primitive types:
- string
- number
- bigint
- boolean
- undefined
- symbol
- null
Except for undefined and null, each primitive is wrapped into a corresponding object to enable property/method access. Attempting to access properties/methods on null or undefined throws errors, proving primitives inherently lack these features.
| Type | Object Wrapper |
|---|---|
| Null | N/A |
| Undefined | N/A |
| Boolean | Boolean |
| Number | Number |
| BigInt | BigInt |
| String | String |
| Symbol | Symbol |
Beyond primitives, JavaScript offers built-in objects like Array and Date to represent collections. All such objects inherit from Object.prototype, including Function.
Thus, JavaScript type checking fundamentally involves inspecting variable primitives and prototypes.
Type Checking Solutions
typeof
typeof returns the type of the operand's value.
Usage
typeof operand
console.log(typeof true);
// Outputs "boolean"
| Type | Result |
|---|---|
| Undefined | "undefined" |
| Null | "object" |
| Boolean | "boolean" |
| Number | "number" |
| BigInt | "bigint" |
| String | "string" |
| Symbol | "symbol" |
| Function | "function" |
| Any other object | "object" |
This is the most direct way to check primitives. Historically, typeof null returns "object" due to a bug: early JavaScript values used a type tag + value structure. Both objects (000 tag) and null (memory address 0x00) shared the 000 tag. The typeof implementation only checks executability (functions are executable, other 000 are objects), causing null misidentification.
For historical context on typeof null, see this article.
Strengths: Simple.
Weaknesses: Cannot check non-function built-in objects or custom objects; requires extra code for null.
instanceof
instanceof checks if a constructor's prototype exists in an object's prototype chain.
Usage
object instanceof constructor
function Man(age) {
this.age = age;
}
const k = new Man(30);
console.log(k instanceof Man); // true
console.log(k instanceof Object); // true (inherits from Object)
When executing object instanceof constructor, JavaScript checks for Symbol.hasInstance on the constructor. If absent, it traverses the prototype chain until Object.prototype.
Symbol.hasInstance can be overridden, leading to inaccurate results:
class FakeArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
console.log([] instanceof FakeArray); // true (custom behavior)
Strengths: Effective for instance type checks.
Weaknesses: Fails for null/undefined; results can be manipulated via Symbol.hasInstance.
constructor
An instance's constructor property references its creating constructor.
Usage
obj.constructor
All objects have a constructor property (unless explicitly set to null). This avoids prototype chain traversal.
Object.prototype.toString.call()
toString() returns an object's string representation. Native Object.prototype.toString returns '[object Type]'.
Exploiting this:
console.log(Object.prototype.toString.call([])); // "[object Array]"
Results can be altered via Symbol.toStringTag:
class FakeArray {
get [Symbol.toStringTag]() {
return 'Array';
}
}
console.log(Object.prototype.toString.call(new FakeArray())); // "[object Array]"
Summary
| Method | Strengths | Weaknesses | Use Cases |
|---|---|---|---|
typeof |
Simple and intuitive; most convenient for primitive checks | Cannot check null or various objects | API data (often primitives) |
instanceof |
Traverses entire prototype chain thoroughly | Cannot check null, undefined or primitives; results can be manipulated | General object type checks |
constructor |
Constructor remains unmodified; accurate | Fails for null-constructor objects; requires constructor comparison; cumbersome for IIFE/anonymous functions | General object type checks |
toString |
Returns descriptive results; easy to use | Verbose code; results can be modified | General object type checks |
No single solution is a "silver bullet." Combining methods is often necessary. Mozilla provides a comprehensive implementation example for reference.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.