for..in цикл и Object.keys отличаются поведением

#javascript

#javascript

Вопрос:

У меня есть этот конструктор объектной функции:

 const Shape = function(name){
this.name = name;
}
Shape.prototype.getName = function(){
    return this.name;
};
  

и у меня есть этот экземпляр

 const square = new Shape("square");
  

Когда я использую цикл for для итерации по квадратному объекту, я вижу, что процесс итерации выполняется над прототипом квадратного объекта

 for (const key in square) console.log(key);
/* #output:
name
getName
*/
  

но когда я использую функцию Object.keys(), я вижу, что процесс итерации не повторяется по объекту-прототипу

 /* #output:
["name"]
*/
  

В чем причина этого за кулисами?

Вот что я пробовал:

Я пытался утешить.зарегистрируйте дескриптор метода getName из объекта-прототипа, я видел, что для атрибута enumerable по умолчанию установлено значение true:

 console.log(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(square), "getName"))

/* #output:
configurable: true
enumerable: true
value: ƒ ()
writable: true
__proto__: Object
*/
  

Ответ №1:

Object.keys выполняет итерацию только по перечислимым собственным свойствам. Напротив, for..in выполняет итерацию по всем перечисляемым свойствам в любом месте цепочки прототипов объекта.

С этим кодом:

 const Shape = function(name){
    this.name = name;
}
Shape.prototype.getName = function(){
    return this.name;
};
  

Shape Экземпляр получает собственное свойство a name , поэтому он повторяется обоими итерационными методами. Напротив, getName находится в прототипе экземпляра — это не свойство самого экземпляра, поэтому оно не возвращается в Object.keys :

 const Shape = function(name){
    this.name = name;
}
Shape.prototype.getName = function(){
    return this.name;
};
const square = new Shape("square");
console.log(
  square.hasOwnProperty('name'),
  square.hasOwnProperty('getName'),
  Shape.prototype.hasOwnProperty('getName')
);  

Ответ №2:

В чем причина этого за кулисами?

for-in перебирает все перечислимые свойства объекта со строковыми именами, включая унаследованные.

Object.keys предоставляет вам только массив собственных (не унаследованных) свойств объекта с перечислимыми строковыми именами. Просто для иллюстрации, это похоже на:

 // Rough, not-exact-in-every-detail implementation of `Object.keys`
// in terms of `for-in`
const result = [];
for (const name in object) {
    if (Object.prototype.hasOwnProperty.call(object, name)) {
        result.push(name);
    }
}
  

Также есть Object.getOwnPropertyNames который предоставляет вам массив всех собственных свойств объектов с именами строк (даже неисчислимых) и Object.getOwnPropertySymbols который предоставляет вам массив собственных свойств объекта с именами символов (даже неисчислимых). (Нет символа, эквивалентного Object.keys .)


Я пытался утешить.зарегистрируйте дескриптор метода getName из объекта-прототипа, я видел, что для атрибута enumerable по умолчанию установлено значение true:

Это когда вы создаете свойство с помощью присваивания, как вы делаете в своем вопросе. Если вы создаете свойство с помощью Object.defineProperty или с помощью синтаксиса метода в class конструкции, по enumerable умолчанию используется значение false .

Примеры:

 // Your original code:
const Shape = function(name){
    this.name = name;
}
Shape.prototype.getName = function(){
    return this.name;
};
showFlag(Shape.prototype, "getName");       // true

// Using `Object.defineProperty`:
const Shape2 = function(name){
    this.name = name;
}
Object.defineProperty(Shape2.prototype, "getName", {
    value: function(){
        return this.name;
    },
    writable: true,     // These also default to `false`, but you
    configurable: true  // usually want them to be `true` for methods
});
showFlag(Shape2.prototype, "getName");      // false 

// Using `class` syntax
class Shape3 {
    constructor(name) {
        this.name = name;
    }
    getName() {
        return this.name;
    }
}
showFlag(Shape3.prototype, "getName");      // false


function showFlag(proto, name) {
    console.log(Object.getOwnPropertyDescriptor(proto, name).enumerable);
}