4、JavaScript 对象分类全解析:99% 前端从没系统梳理过

113 阅读3分钟

在日常开发中,我们习惯性地写:

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) :随着运行时自动创建,例如 MathReflectJSON
  • 原生对象(Native Objects) :可通过构造器调用产生,例如 ArrayMapSet

这些对象是 JS 的“基石”,不依赖宿主环境就存在。


4. 宿主对象(Host Objects)

由宿主环境提供,行为完全由宿主决定。

在浏览器环境里:

window.document
document.createElement("canvas")
new Image()

这些对象的实现逻辑在浏览器内部,JS 本身并没办法纯粹模拟。

在 Node.js 里,processBuffer 也是典型宿主对象。


5. 函数对象 & 构造器对象

JS 是用对象来模拟函数的语言。

  • 函数对象:具有 [[call]] 内部方法,可以被直接调用。
  • 构造器对象:具有 [[construct]] 内部方法,可以配合 new 使用。

示例:

function f() { return 1; }
f();       // 作为函数调用
new f();   // 作为构造器调用

注意:箭头函数没有 [[construct]],所以不能被 new


6. 特殊行为的对象

有些对象有着与众不同的特性:

  • Arraylength 会随下标变化。
  • 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 的运行时模型。

互动问题
👉 你能说出多少种创建对象的方法?
欢迎留言挑战,看看谁找得最多!