在日常开发中,我们习惯性地写:
let o = {};
let arr = [];
let d = document.createElement("div");
但你有没有想过:
👉 这些对象其实不是同一种类型!
JavaScript 的对象系统比你想象的复杂得多。今天我们就来一次彻底梳理,看看 JS 世界里的对象到底分几类。
1. 普通对象(Ordinary Objects)
最常见的,就是用字面量、Object 构造器或 class 定义出来的对象:
let o1 = {};
let o2 = new Object();
class C {}
let o3 = new C();
它们可以被原型继承,是我们日常写业务代码最常打交道的“普通对象”。
2. 原生对象(Native Objects)
通过语言内置构造器创建的,比如:
new Array();
new RegExp();
new Date();
这些构造器大多有一些“底层特权”,无法用纯 JS 代码模拟。
比如数组的 length 属性会自动随下标变化,这是字面量对象无法实现的。
3. 内置对象(Built-in Objects)
JS 语言规范直接提供的,包括:
- 固有对象(Intrinsic Objects) :随着运行时自动创建,例如
Math、Reflect、JSON。 - 原生对象(Native Objects) :可通过构造器调用产生,例如
Array、Map、Set。
这些对象是 JS 的“基石”,不依赖宿主环境就存在。
4. 宿主对象(Host Objects)
由宿主环境提供,行为完全由宿主决定。
在浏览器环境里:
window.document
document.createElement("canvas")
new Image()
这些对象的实现逻辑在浏览器内部,JS 本身并没办法纯粹模拟。
在 Node.js 里,process、Buffer 也是典型宿主对象。
5. 函数对象 & 构造器对象
JS 是用对象来模拟函数的语言。
- 函数对象:具有
[[call]]内部方法,可以被直接调用。 - 构造器对象:具有
[[construct]]内部方法,可以配合new使用。
示例:
function f() { return 1; }
f(); // 作为函数调用
new f(); // 作为构造器调用
注意:箭头函数没有 [[construct]],所以不能被 new。
6. 特殊行为的对象
有些对象有着与众不同的特性:
- Array:
length会随下标变化。 - String:下标访问的是字符,而不是普通属性。
- arguments:下标属性和形参联动。
- 模块 namespace:只读且行为特殊。
- TypedArray & ArrayBuffer:直接和底层内存块绑定。
这些对象往往带有引擎层面的优化或约束。
7. 小实验:如何枚举所有固有对象?
利用广度优先搜索,从全局对象开始,把所有属性都抓出来,就能统计出 JS 环境中的固有对象总数。
简化版代码:
let set = new Set();
let queue = [globalThis];
while (queue.length) {
let o = queue.shift();
Object.getOwnPropertyNames(o).forEach(p => {
let v = o[p];
if (typeof v === "object" || typeof v === "function") {
if (!set.has(v)) { set.add(v); queue.push(v); }
}
});
}
console.log(set.size);
打印试试,是不是很硬核?
写在最后
JavaScript 的对象分类远不止 {} 和 []。
- 普通对象是我们写业务的主角;
- 原生 & 内置对象是语言基石;
- 宿主对象让 JS 真正能和浏览器/Node 交互;
- 特殊对象则承载了语言的“黑魔法”。
理解这些,才能真正吃透 JavaScript 的运行时模型。
互动问题:
👉 你能说出多少种创建对象的方法?
欢迎留言挑战,看看谁找得最多!