null and undefined
From the previous article in this series, we learned that undefined == null
returns true when performing an equality comparison. However, the ES5 specification doesn’t seem to clearly explain why this happens, which aroused my intense curiosity. Through this article, we will unveil this mystery.
What is undefined
undefined is a primitive data type in JavaScript that represents an “undefined” state. It exists as a global property and is used to indicate that a variable hasn’t been assigned a value or a function hasn’t returned a value.
When a variable is declared without being initialized, its default value is undefined.
let x;
console.log(x); // Output: undefined
If a function has no return statement, or the return statement doesn’t return any value, then the function’s return value is undefined.
function foo() {}
console.log(foo()); // Output: undefined
If a function is called without passing the defined parameters, those parameters will have the value undefined.
function bar(a, b) {
console.log(a); // 10
console.log(b); // undefined
}
bar(10);
In summary, undefined is used to represent a “not yet defined” state, typically occurring when variables are declared, functions don’t return values, or parameters aren’t passed.
What is null
null specifically indicates that an object’s value hasn’t been set, and it’s one of JavaScript’s primitive data types. In boolean operations, null is considered falsy, meaning it will be converted to false in a boolean context.
console.log(Boolean(null)); // Output: false
null is a literal, unlike undefined, it’s not a property of the global object. null indicates that a variable isn’t pointing to any object, and can be understood as an “object not yet created”. Logically, null represents an empty object pointer, which explains why typeof null returns “object”. This historical artifact stems from early JavaScript design, where null was treated in memory as a pointer to an empty object.
console.log(typeof null); // Output: "object"
When getting DOM elements, if no matching element is found, null is returned. This indicates there’s no matching element to operate on.
const element = document.getElementById('nonexistent');
console.log(element); // Output: null
The top of the prototype chain is null. This means that JavaScript objects’ prototype chain ultimately points to null, indicating the end of the prototype chain.
const obj = {};
console.log(Object.getPrototypeOf(obj)); // Output: null
In summary, null is used to represent an unset object value, indicating an empty object pointer. It’s a common JavaScript primitive data type, frequently used to represent “none” or “empty” states.
Why typeof null Returns “object”
- When using
typeof
to check the types ofundefined
andnull
, see the following code:
console.log(typeof undefined); // undefined
console.log(typeof null); // object
The output of typeof undefined
as "undefined"
is probably well-known, but why does typeof null
output "object"
? Through research, this is known to be a historical bug. There was once an ECMAScript proposal to fix this, but it was rejected as it would break existing code (projects that rely on this bug…).
In JavaScript’s first version, values were stored in 32-bit units (equivalent to a machine code) consisting of a small type tag (1-3 bits) and the actual data of the value. The type tag was stored in the lower bits, where:
- 000:
object
, data is a reference to an object - 1:
integer
, data is a 31-bit signed integer - 010:
double
, data is a reference to a double-precision float - 100: string, data is a string
- 110: boolean, data is a boolean value
In the V8 engine, JavaScript hot code is compiled into machine code, which is directly read and executed by the computer’s CPU. It runs fastest but is very obscure and difficult to write; machine code is code that computers can directly execute and runs at the highest speed.
undefined and null are special:
- undefined: is the integer
(-2)^30
(a number beyond the integer range) - null: is the machine code
NULL
pointer, or an object type tag plus a reference of 0
Now, back to our question of why typeof null
returns "object"
. It should be easier to understand now.
Because the object type tag is 0
, and since null
represents an empty pointer
(JavaScript has no concept of pointers), null is represented by all 32 bits being 0
. Therefore, typeof null
returns "object"
.
So from the above summary, null
is essentially 0
, as shown in the following example:
console.log(undefined + 1); // NaN
console.log(null + 1); // 1
When null
is converted to a number
type, it becomes 0
.
When undefined
is converted to a number
type, it becomes NaN
.
So what other differences are there in usage? Let’s continue with code:
function foo(x = 77, y = 'moment') {
console.log(x); // 77
console.log(y); // null
}
foo(undefined, null);
When calling foo(undefined, null):
- x’s value is undefined, so the default value 77 is assigned to x.
- y’s value is null, so y is set to null instead of the default value “moment”.
Here’s an example of destructuring assignment:
const [x = 1, y = 2] = [undefined, null];
console.log(x); // Output: 1
console.log(y); // Output: null
In this example:
- x’s value is undefined, so it takes the default value 1.
- y’s value is null, so it’s directly assigned null instead of taking the default value 2.
References
Conclusion
null
and undefined
have different meanings and uses in JavaScript. null
represents an empty object pointer, while undefined
represents an undefined state. typeof null
returning "object"
is a historical artifact from JavaScript’s early memory management mechanism. null
converts to 0
when converted to a number, while undefined
converts to NaN
. In function parameters, undefined
triggers default values, while null
is directly assigned to parameters without triggering default values.