为什么不同方式创建的JS对象属性无法被console.log正常输出?
JavaScript对象属性访问与console打印异常解答
核心原理前置
console.log在Node.js、Chrome等基于V8引擎的环境中,打印普通对象时默认仅遍历展示对象自身的可枚举属性,不会默认展示不可枚举的自有属性、原型链上的属性(部分调试环境的实现会额外展示原型内容,因此会出现跨环境表现不一致的情况)。- 使用
Object.defineProperty定义属性时,若未显式指定描述符配置,writable、enumerable、configurable三个布尔字段的默认值均为false。其中enumerable字段决定属性是否可被枚举遍历。 Object.create(proto)方法会创建一个空的新对象,将传入的第一个参数设置为新对象的原型(__proto__),挂载在原型上的属性不属于新对象的自有属性。Object.getOwnPropertyDescriptor仅会查找对象的自有属性,不会沿原型链查找;而通过.运算符访问属性时,会沿原型链向上查找直到找到对应属性或到达原型链顶端。
复现代码逐段说明
MyObject(字面量定义)
通过对象字面量直接声明的属性,默认writable、enumerable、configurable均为true,属于对象自身的可枚举属性,因此console.log可以正常打印所有属性,符合预期。
const MyObject = {text: null}; console.log("MyObject: ", MyObject); // 正常输出{ text: null }
MyObject2(defineProperty定义)
调用Object.defineProperty时未显式配置enumerable: true,因此text属性虽然是MyObject2的自有属性,但是是不可枚举属性:
- 直接通过
.访问属性、调用Object.getOwnPropertyDescriptor都可以正常获取结果,因为属性真实存在于对象自身 - console.log遍历可枚举属性时会跳过该属性,因此打印对象时看不到
text字段
const MyObject2 = {}; Object.defineProperty(MyObject2, 'text' , {value: null}); console.log(MyObject2) // 输出{},因为text不可枚举 console.log(MyObject2.text) // 输出null,属性真实存在 console.log(Object.getOwnPropertyDescriptor(MyObject2, 'text')); // 输出{ value: null, writable: false, enumerable: false, configurable: false }
修复方式:定义属性时显式加上
enumerable: true配置,即可让属性正常被console.log打印。
MyObject3(Object.create创建)
传入Object.create的{text: null}会被设为新对象的原型,text属性挂载在原型链上,不属于MyObject3的自有属性:
- 直接通过
.访问text能拿到null,是JS原型链向上查找的正常机制 Object.getOwnPropertyDescriptor仅查找自有属性,因此返回undefined- 不同环境console.log实现逻辑存在差异:部分环境仅展示自身可枚举属性,因此打印空对象
{};部分调试环境会额外展开原型链内容,因此能看到text字段,这就是跨环境表现不一致的原因
const MyObject3 = Object.create({text: null}); console.log(MyObject3) // 表现随环境变化,可能显示{},也可能在原型部分展示text console.log(MyObject3.text) // 输出null,沿原型链查找得到结果 console.log(Object.getOwnPropertyDescriptor(MyObject3, 'text')); // 输出undefined,text不是自有属性
修复方式:如果需要text作为对象自身属性,要么创建对象后直接给实例赋值,要么使用
Object.create的第二个参数定义自有属性(注意同样需要显式配置enumerable: true)。
常用验证方法
- 判断属性是否为对象自有属性:使用
Object.hasOwn(目标对象, 属性名),旧环境可使用Object.prototype.hasOwnProperty.call(目标对象, 属性名) - 获取对象所有自有属性(包含不可枚举属性):使用
Object.getOwnPropertyNames(目标对象) - 获取对象所有可枚举属性(包含原型链上的可枚举属性):使用
for...in循环遍历
内容的提问来源于stack exchange,提问作者Sani4
相关产品推荐
相关产品推荐

