Почему getter / setter больше не работают после копирования объекта с распространенным синтаксисом?

1

В следующем фрагменте синтаксис распространения работает таким образом, что я не совсем понимаю:

let obj = {
  set setName(name){
    obj.name = name
  },
  get myName() {
    return obj.name
  }
}
    
obj.setName = 'Jon Doe'

let spread_obj = {...obj}
spread_obj.setName = 'Marion Luke'
console.log('spread_obj name', spread_obj.myName) // spread_obj name Jon Doe 

let create_obj = Object.create(obj)
create_obj.setName = 'Marion Luke'
console.log('create_obj name', create_obj.myName) // create_obj name Marion Luke

Можете ли вы объяснить, почему переназначение имени не работает в таком случае?

Теги:
object
spread-syntax

3 ответа

4
Лучший ответ

Распространение объекта не копирует методы getter и setter - скорее, операция распространения получает только свойство (то есть, оно вызывает геттер, если оно есть), и присваивает результат результирующему объекту (spread_obj, здесь), как обычный объект, без каких-либо геттеров или сеттеров. Вы можете видеть это, если вы ставите логические операторы внутри сеттера и получателя. В результате spread_obj имеет свойство setName undefined потому что setName - это только метод setter, который ничего не возвращает.

let obj = {
  set setName(name){
    console.log('setting ' + name);
    this.name = name
  },
  get myName() {
    console.log('getting');
    return this.name
  }
}

obj.setName = 'Jon Doe'
console.log('About to spread');
let spread_obj = {...obj}
console.log('Done spreading. spread_obj contents is:');
console.log(spread_obj);
spread_obj.setName = 'Marion Luke' // No "setting" log statement

Также обратите внимание, что если вы хотите использовать вторую часть своего кода, вы, вероятно, должны изменить методы setter и getter, чтобы ссылаться на this а не на obj, иначе результаты могут быть неинтуитивными; ваша область методов getter и setter всегда ссылается на свойство .name на obj, а не на свойство .name на стандартный контекст вызова (то есть на spread_obj или на create_obj). Когда вы пытаетесь назначить spread_obj.setName, вы фактически изменяете исходное свойство obj .name:

let obj = {
  set setName(name){
    obj.name = name
  },
  get myName() {
    return obj.name
  }
}
obj.setName = 'Jon Doe'

let create_obj1 = Object.create(obj)
create_obj1.setName = 'Marion Luke1'

let create_obj2 = Object.create(obj)
create_obj2.setName = 'Marion Luke2'
// "Marion Luke2", but we're logging the property from the "first" object!
console.log('create_obj name', create_obj1.name)

Исправьте, обратившись к this вместо obj:

let obj = {
  set setName(name){
    this.name = name
  },
  get myName() {
    return this.name
  }
}
obj.setName = 'Jon Doe'

let create_obj1 = Object.create(obj)
create_obj1.setName = 'Marion Luke1'

let create_obj2 = Object.create(obj)
create_obj2.setName = 'Marion Luke2'
console.log(create_obj1.name)
  • 0
    Я использовал obj вместо этого, потому что я думал, что сеттеры и геттеры на самом деле копируются. теперь я понял, что что-то вроде Object.assign(SomeClassInstance.prototype, obj) тоже не будет работать.
1

В дополнение к объяснению @CertainPerformances я бы добавил, что поведение кажется более разумным, если использовать геттеры/сеттеры более традиционно, имея одно имя для get и set.

Например, когда ваш объект выглядит так, все работает немного лучше (по крайней мере, на поверхности):

let obj = {
  set name(name){        // setter and getter are accessed with the same property
    this._name = name
  },
  get name() {
    return this._name
  }
}

obj.name = 'Jon Doe'
let spread_obj = {...obj}
spread_obj.name = 'Marion Luke'

// this seems more expected
console.log('spread_obj name', spread_obj.name)

// but this is still a little strange because
// Jon Doe is still there
console.log(spread_obj)

Это похоже на большую работу, но поскольку распространение объекта имеет только перечислимые свойства, вы можете сделать _name неперечислимым. Тогда все кажется немного более разумным:

let obj = {
  set name(name){
    this._name = name
  },
  get name() {
    return this._name
  }
}
Object.defineProperty(obj, '_name', {
  enumerable: false,
  writable: true,
  configurable: true
});

obj.name = 'Jon Doe'

let spread_obj = {...obj}
spread_obj.name = 'Marion Luke'

// now spread_obj only has a name prop:
console.log(spread_obj)

// and obj too:
console.log(obj)
0

выбранный ответ неверен. getter/setter - это не метод, это специальные свойства.... spread и object.assign не будут работать, просто потому, что когда они копируют, они обрабатывают getter/setter как обычное перечисляемое свойство, они копируют его "значение", поэтому getter/setter завершится ошибкой. если вы хотите скопировать его, вы можете обратиться к undercore.js:

 descriptor = Object.getOwnPropertyDescriptor(source, prop);
                Object.defineProperty(obj, prop, descriptor);

Ещё вопросы

Сообщество Overcoder
Наверх
Меню