实验代码

// 创建对象x,并分别赋值可遍历与不可遍历属性
let x = {}
x.name = 'tl'
Object.defineProperty(x, 'age', {
    value: 17,
    writable: true,
    enumerable: false,
    configurable: true
})
// 验证是否能遍历symbol属性的键名
Object.defineProperty(x, Symbol('symbolKey'), {
    value: 'symbolVal',
    writable: true,
    enumerable: false,	// 确认与enumerable属性无关
    configurable: true
})


// 创建x的原型对象p,并分别赋值可遍历与不可遍历属性
let p = {}
p.sex = 'm'
Object.defineProperty(p, 'job', {
    value: 'engineer',
    writable: true,
    enumerable: false,
    configurable: true
})

x.__proto__ = p

in

'name' in x		// true
'age' in x		// true
'sex' in x		// true
'job' in x		// true
  • 结论:对于in操作符,只要通过该对象可以访问该属性(在自身访问或通过原型链访问)即返回true,因为 in 操作符没有进行枚举,因此不受enumerable值的影响

for - in

for (let key in x) {
	console.log(key)
}
// name
// sex
  • 结论:拥有in操作符的特性,只要通过该对象可以访问该属性即可以遍历,因此可以得到原型链上的key,同时由于for是明显的枚举操作,因此会受到enumerable值的影响,即不可遍历enumerablefalse的值。即循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)。

Object.keys

Object.keys(x)	// ["name"]
  • 结论:只可访问对象自身的key,同时由于是遍历操作,因此会受到enumerable值的影响,即不可遍历enumerablefalse的值,返回一个key组成的数组。即对象自身的(不含继承的)所有可枚举属性(不含 Symbol 属性)的键名。

Object.getOwnPropertyNames

Object.getOwnPropertyNames(x)	// ["name", "age"]
  • 结论:只可访问对象自身的key, 但是包括不可枚举的属性,即对象自身的所有属性(不含 Symbol 属性,但是包括不可枚举属性)的键名。

Object.getOwnPropertySymbols

Object.getOwnPropertySymbols(x)	// [Symbol(symbolKey)]
  • 结论:返回一个数组,包含对象自身的所有 Symbol 属性的键名。(与enumerable无关)

Reflect.ownKeys

Reflect.ownKeys(x)	// ["name", "age", Symbol(symbolKey)]
  • 结论:返回一个数组,包含对象自身的(不含继承的)所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。