JavaScript对象存在隐藏内部属性,如[[Prototype]]决定继承链、[[Callable]]和[[Constructor]]标识可调用性与可构造性、[[Extensible]]等控制可变性,以及Symbol.toStringTag影响类型识别。

JavaScript对象确实存在一些不直接暴露在代码中的隐藏属性,它们影响对象的行为和内部结构,但无法通过常规属性访问方式(如 obj.prop 或 Object.keys())看到。这些隐藏属性不是ECMAScript规范中明确定义的“属性”,而是引擎实现层面的内部状态或元信息,常被用于原型链查找、属性访问、类型判断等底层逻辑。
隐式原型([[Prototype]])
每个对象内部都有一个 [[Prototype]] 隐藏引用,指向其原型对象(通常是 constructor.prototype)。它决定了属性继承与方法查找路径,但不能直接读写——obj.[[Prototype]] 语法非法。可通过 Object.getPrototypeOf(obj) 或非标准但广泛支持的 obj.__proto__ 访问(仅作调试用,生产环境避免使用)。
- 函数对象的
[[Prototype]]默认指向Function.prototype - 数组对象的
[[Prototype]]指向Array.prototype - 普通对象字面量的
[[Prototype]]指向Object.prototype
构造器与可调用性标记([[Callable]] 和 [[Constructor]])
函数对象拥有 [[Callable]] 内部槽,表示可被调用;若同时具备 [[Constructor]],则还能用 new 实例化。普通对象没有这两个槽,因此调用会报 TypeError。这类标记不可枚举、不可配置,也无法通过 JavaScript 代码直接检测,但可通过行为间接判断:
-
typeof fn === 'function'只说明有[[Callable]],不保证可new -
fn.prototype && typeof fn.prototype.constructor === 'function'是常见启发式判断,但不绝对可靠(箭头函数无prototype,类方法也不一定有) - 真正安全的方式是尝试
new fn()并捕获异常,或使用Reflect.construct()(需已知参数)
对象的内部标签([[Extensible]]、[[Sealed]]、[[Frozen]])
这些是控制对象可变性的内部状态,决定能否添加/删除/修改属性。它们不暴露为属性,但可通过 Object.isExtensible()、Object.isSealed()、Object.isFrozen() 检测。
立即学习“Java免费学习笔记(深入)”;
-
[[Extensible]]为false时,不能再添加新属性(包括自有属性和原型属性) -
[[Sealed]]是[[Extensible]]为false且所有现有属性为configurable: false -
[[Frozen]]还进一步将所有数据属性设为writable: false
Symbol.toStringTag 与内置对象识别逻辑
虽然 Symbol.toStringTag 是一个可写的 Symbol 属性(非完全隐藏),但它被许多内置方法(如 Object.prototype.toString.call())用来生成标准字符串标签,是 JS 引擎识别对象“类型”的关键依据。
-
Object.prototype.toString.call([])返回"[object Array]",因为数组的toStringTag值为"Array" - 自定义类可设置
static get [Symbol.toStringTag]() { return 'MyClass'; }来影响toString()输出 - 注意:
instanceof依赖[[Prototype]]链,而toString依赖toStringTag,二者逻辑不同
这些隐藏机制共同支撑了 JavaScript 的动态性和灵活性,理解它们有助于写出更健壮的类型判断、深拷贝、代理拦截等逻辑。实际开发中不必手动操作这些内部槽,但掌握其存在和检测方式,能避开常见陷阱,比如误判函数可构造性、忽略对象冻结状态导致静默失败等。


















