Object.getOwnPropertyNames和Object.keys的区别

304

Object.getOwnPropertyNamesObject.keys在JavaScript中有什么区别?同时,一些示例会很受欢迎。


4
从MDN的两篇文章来看,它们之间的区别在于返回的列表是否包括不可枚举属性:Object.keys() 不会返回它们,而 Object.getOwnPropertyNames() 会返回。 - Sirko
6
MDN: 属性的枚举性和所有权在JavaScript中,对象属性可以具有枚举性和所有权。枚举性决定了属性是否可以通过for...in循环或Object.keys()方法来枚举。所有权则决定了属性是否可以被删除或修改。默认情况下,通过字面量创建的对象属性是可枚举的,而通过Object.defineProperty()方法创建的属性是不可枚举的。可以使用Object.getOwnPropertyDescriptor()方法来获取属性的描述符,并检查属性的枚举性和所有权。对于原型链上的属性,只有它们的可枚举属性才会被枚举。如果需要枚举所有属性,可以使用Object.getOwnPropertyNames()或Reflect.ownKeys()方法。当一个属性被添加到对象上时,它会继承该对象的所有权。可以使用delete运算符删除对象上的属性,并使用Object.assign()方法将其他对象的属性复制到目标对象上。请注意,枚举性和所有权只关注对象的属性,而不是其值。 - Bergi
5个回答

390

有一点不同。Object.getOwnPropertyNames(a)返回对象a的所有自有属性。Object.keys(a)返回所有可枚举的自有属性。这意味着,如果您定义对象属性时没有将其中一些设置为enumerable: false,那么这两种方法将给出相同的结果。

测试很容易:

var a = {};
Object.defineProperties(a, {
    one: {enumerable: true, value: 1},
    two: {enumerable: false, value: 2},
});
Object.keys(a); // ["one"]
Object.getOwnPropertyNames(a); // ["one", "two"]

如果您定义一个属性而没有提供属性描述符(也就是说,您没有使用Object.defineProperties),例如:
a.test = 21;

那么这样的属性将自动变为可枚举,并将出现在两个方法返回的数组中。


48
特别是,数组对象的 length 属性不可枚举,因此它不会显示在 Object.keys 中。 - Barmar
10
对象的length属性存在于原型上而不是对象本身,因此Object.keysObject.getOwnPropertyNames都无法列出它。 - The Qodesmith
14
Object.getOwnPropertyNames(anyArray) 的结果包括 length - thorn0
9
我明白了!"Object.getOwnPropertyNames(anyArray)" 确实会在返回的数组中包含 "length"! - The Qodesmith
自定义 length 属性不可枚举:Object.getOwnPropertyDescriptor([], 'length') - zurfyx
显示剩余2条评论

36

另一个区别在于数组的情况下,Object.getOwnPropertyNames方法将返回一个额外的属性,即length

var x = ["a", "b", "c", "d"];
Object.keys(x);  //[ '0', '1', '2', '3' ]
Object.getOwnPropertyNames(x);  //[ '0', '1', '2', '3', 'length' ]

6

直接量和构造函数在创建对象时的区别。 这是令我困惑的事情。

const cat1 = {
    eat() {},
    sleep() {},
    talk() {}
};

// here the methods will be part of the Cat Prototype
class Cat {
    eat() {}
    sleep() {}
    talk() {}
}

const cat2 = new Cat()

Object.keys(cat1) // ["eat", "sleep", "talk"]
Object.keys(Object.getPrototypeOf(cat2)) // []

Object.getOwnPropertyNames(cat1) // ["eat", "sleep", "talk"]
Object.getOwnPropertyNames(Object.getPrototypeOf(cat2)) // ["eat", "sleep", "talk"]

cat1 // {eat: function, sleep: function, talk: function}
cat2 // Cat {}

// a partial of a function that is used to do some magic redeclaration of props
function foo(Obj) {
    var propNames = Object.keys(Obj);

    // I was missing this if
    // if (propNames.length === 0) {
    //     propNames = Object.getOwnPropertyNames(Obj);
    // }

    for (var prop in propNames) {
        var propName = propNames[prop];

        APIObject[propName] = "reasign/redefine or sth";
    }
}

在我的案例中,如果我给foo函数cat2类型的对象,它就无法正常工作。

还有其他创建对象的方式,可能也存在其他问题。


Object.getOwnPropertyNames 将返回 cat1 的属性名称而不是 cat2。创建对象的两种方式在 Object.getOwnPropertyNamesObject.keys 之间没有区别。 - Boric
1
@Boric 是的,你说得对。我可能本意是要在两个方法中都使用 Object.getPrototypeOf(cat2),而不仅仅是 cat2。我不能确定,因为我不记得也没有代码。我会在答案中修复它。 - h3dkandi

2

如之前所解释的那样,.keys 不返回不可枚举的属性。

关于示例,一个陷阱案例是 Error 对象: 它的某些属性是不可枚举的。
因此,当使用 console.log(Object.keys(new Error('some msg'))) 时会返回 [], 而使用 console.log(Object.getOwnPropertyNames(new Error('some msg'))) 则会返回 ["stack", "message"]

console.log(Object.keys(new Error('some msg')));
console.log(Object.getOwnPropertyNames(new Error('some msg')));


-6
另一个区别是(至少在nodejs中),“getOwnPropertyNames”函数不保证键的顺序,这就是为什么我通常使用“keys”函数的原因:
    Object.keys(o).forEach(function(k) {
      if (!o.propertyIsEnumerable(k)) return;
      // do something...
    });

1
在当前版本的Node.js中是否仍然是这种情况,如果是,您是否知道任何getOwnPropertyNames无序的示例?因为ES2015指定了Obect.getOwnPropertyNames的顺序,而Obect.keys的顺序仍取决于实现。 - szupie
8
我一直认为JS对象键的顺序是不确定的,即使一个实现保留了顺序,你也不应该依赖它? - Ruan Mendes
1
嗯,我认为是相反的;getOwnPropertyNames 的顺序在规范中定义。Object.keys 取决于实现。 - tjvr
1
以下所有内容均没有指定顺序:for-in 循环Object.keysObject.getOwnPropertyNames。尽管如此,这三个方法枚举的顺序是相互一致的。 - Thomas Eding

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接