Header Ads Widget

Ticker

6/recent/ticker-posts

Copiar propiedades de un objeto a otro (incluidos Getters y Setters)

 Object.assignes la forma estándar de copiar propiedades de un objeto a otro. A menudo se utiliza para copiar propiedades de una capa de profundidad. (Una capa de profundidad significa que no hay objetos anidados).

Se puede utilizar para ampliar la configuración de un objeto predeterminado. He aquí un ejemplo:

const one = { one: 'one' }
const two = { two: 'two' }
const merged = Object.assign({}, one, two)

console.log(merged) // { one: 'one', two: 'two' }

Desafortunadamente, Object.assignno copia los descriptores de acceso. (Accessor es un término para las funciones Getter y Setter). Object.assignlee el valor de una función Getter y copia ese valor en su lugar.

let count = 0
const one = {}
const two = {
  get count () { return count },
  set count (value) { count = value }
}
const three = Object.assign({}, one, two)

console.log('two:', two)
console.log('three:', three)

Intente iniciar sesión twothreeen un entorno de nodo. Los accesos se registrarán claramente. Inmediatamente verá que three.countNO es un acceso.

Los descriptores de acceso no se copian en tres.

Copia de accesos

El artículo de MDN sobre lo Object.assignafirma. Si desea copiar los descriptores de acceso, debe:

  1. Obtenga el descriptor de la propiedad con Object.getOwnPropertyDescriptor
  2. Crea una propiedad con Object.defineProperty

Object.getOwnPropertyDescriptor

Object.getOwnPropertyDescriptorle brinda más información sobre una propiedad. Esta informacion incluye:

  1. value: Valor de la propiedad (si corresponde)
  2. get: Función getter (si corresponde)
  3. set: Función de setter (si existe)
  4. writable: Si la propiedad se puede editar
  5. configurable: Si la propiedad se puede editar y eliminar
  6. enumerable: Si la propiedad se puede enumerar

No necesitamos para utilizar las funciones avanzadas como writableconfigurableenumerablenormalmente. Así que no es necesario utilizar getPropertyDescriptormucho en la práctica.

Sintaxis:

const descriptor = Object.getOwnPropertyDescriptor(object, 'property')

Si toma una propiedad normal, verá una valueclave.

const object = {
  normalProperty: 'hello world',
}

const descriptor = Object.getOwnPropertyDescriptor(object, 'normalProperty')
console.log(descriptor)
// Output
// {
//   value: 'hello world',
//   writable: true,
//   enumerable: true,
//   configurable: true
// }
Descriptor de una propiedad normal.

Si registra el descriptor de un descriptor de acceso, verá las teclas getset.

let count = 0
const two = {
  get count () { return count }
  set count (value) { count = value }
}

const descriptor = Object.getOwnPropertyDescriptor(two, 'count')
console.log(descriptor)
Descriptor de un descriptor de acceso.

Object.getDefineProperty

Object.definePropertyle permite crear una propiedad. Te permite configurar los mismos 6 valores que encuentras en Object.getOwnPropertyDescriptor.

  1. value: Valor de la propiedad (si corresponde)
  2. get: Función getter (si corresponde)
  3. set: Función de setter (si existe)
  4. writable: Si la propiedad se puede editar
  5. configurable: Si la propiedad se puede editar y eliminar
  6. enumerable: Si la propiedad se puede enumerar

Object.defineProperty solo se puede utilizar una vez creado el objeto.

Sintaxis:

Object.defineProperty(object, property, desciptor)

Ejemplo:

const object = {}
Object.defineProperty(object, 'normalProperty', { value: 'Hello world'})

console.log(object) // { normalProperty: 'Hello world' }

No hay necesidad de utilizar Object.definePropertypara las propiedades normales, a menos que desee cambiar los writableconfigurableenumerableajustes.

Si simplemente necesita crear una propiedad con un valor, puede usar la notación a la que estamos acostumbrados:

// Same result as above
const object = {}
object.normalProperty = 'Hello world'

Object.definePropertyes útil cuando necesita crear descriptores de acceso DESPUÉS de que se crea un objeto. Esto se debe a que las abreviaturas de descriptores de acceso solo se pueden utilizar al crear el objeto. No se pueden utilizar posteriormente.

// Creating a `count` getter function with Accessor shorthands
const object = {
  get count () {}
}

Si desea agregar un descriptor de acceso a un objeto definido, necesita Object.defineProperty

// Same result as above
const object = {}
Object.defineProperty(object, 'count', {
  get function () {
    return count
  }
}

Copia de accesos

Si queremos copiar un descriptor de acceso de un objeto a otro, podemos:

  1. Obtén el descriptor con Object.getOwnPropertyDescriptor
  2. Crea la propiedad con Object.defineProperty

He aquí un ejemplo:

let count
const original = {
  get count () { return count },
  set count (value) { count = value }
}
const copy = {}

const descriptor = Object.getOwnPropertyDescriptor(original, 'count')
Object.defineProperty(copy, 'count', descriptor)

console.log('copy:', copy)
Se copió el descriptor de acceso `count`.

Copiar todas las propiedades de un objeto

Es fácil copiar todas las propiedades de un objeto una vez que sepa cómo copiar uno. Puede recorrer todas las propiedades enumerables y ejecutar las mismas dos líneas de código.

const original = {
  normalProperty: 'hello world',
  get count () { return count },
  set count (value) { count = value }
}
const copy = {}

// Copies all properties from original to copy
const props = Object.keys(original)
for (const prop of props) {
  const descriptor = Object.getOwnPropertyDescriptor(original, prop)
  Object.defineProperty(copy, prop, descriptor)
}

console.log('copy:', copy)
Copiaron todas las propiedades, incluidos los descriptores de acceso.

Fusionar diferentes fuentes de objetos

Si queremos copiar propiedades de múltiples fuentes, necesitamos crear una función que tome todas las fuentes posibles. Llamemos a esta función mix.

function mix (...sources) {
  // ...
}

Luego recorreremos cada fuente y copiaremos las propiedades en un nuevo objeto.

function mix (...sources) {
  const result = {}
  for (const source of sources) {
    const props = Object.keys(source)
    for (const prop of props) {
      const descriptor = Object.getOwnPropertyDescriptor(source, prop)
      Object.defineProperty(result, prop, descriptor)
    }
  }
  return result
}

mixse puede utilizar como Object.assignahora.

let count = 0
const one = { one: 'one' }
const two = { two: 'two' }
const three = {
  get count () { return count },
  set count (value) { count = value }
}
const mixed = mix({}, one, two, three)

console.log('mixed:', mixed)
Propiedades y accesos combinados en un nuevo objeto con mezcla

La gran parte es mixque no muta los objetos. No tienes que pasar un objeto vacío.

// Produces the same result as above
const mixed = mix(one, two, three)

Fusión superficial vs Fusión profunda

Object.assignno funciona bien con objetos anidados. Si copia un objeto anidado, ese objeto anidado aún se puede mutar.

const one = {}
const two = { nested: { value: 'two' } }
const three = Object.assign({}, one, two)

// Nested values are mutated when changed
three.nested.value = 'three'
console.log(two.nested.value) // 'three'

Nuestra mixfunción funciona de la misma manera que Object.assignEso no es ideal.

// Same result as above
const one = {}
const two = { nested: { value: 'two' } }
const three = mix(one, two)

// Nested values are mutated when changed
three.nested.value = 'three'
console.log(two.nested.value) // 'three'

Ambos Object.assignmixrealizan lo que llamamos una fusión superficial . Una fusión superficial es cuando copia y pega las propiedades de la primera capa completamente en un nuevo objeto. Las propiedades que pertenecen a un objeto anidado siguen apuntando a la misma referencia.

Nota: si confunde las “referencias”, lea esta analogía sobre las tarjetas de identidad . Aclarará las cosas.

No queremos que los objetos anidados apunten a las mismas referencias porque pueden mutar sin que lo sepamos. Este tipo de mutación es una fuente de errores difíciles de encontrar . En su lugar, queremos realizar una fusión profunda (donde creamos nuevas versiones de objetos anidados en el nuevo objeto).

Formas de fusión profunda

Muchas personas ya han creado formas de realizar una fusión profunda. Ejemplos incluyen:

  1. Cesión de Nicolás Bevacqua
  2. Opciones de combinación de Michael Mayer
  3. Deepmerge de Josh Duff

Estas bibliotecas funcionan como Object.assign.

  1. Pasas una lista de objetos separados por comas para fusionar.
  2. La biblioteca fusionará el objeto y devolverá un nuevo objeto.

Sin embargo, existen ligeras diferencias.

assignmentfunciona exactamente igual Object.assignEl primer objeto por el que pasó será mutado. Entonces necesitas pasar un objeto vacío.

const one = {}
const two = { nested: { value: 'two' } }
const three = assignment({}, one, two)

merge-optionsdeepmergecrea un objeto vacío automáticamente. Por lo tanto, no tiene que pasar un objeto vacío como primer argumento.

const mergeOoptions = require('merge-options')

const one = {}
const two = { nested: { value: 'two' } }
const three = mergeOptions(one, two)

Mientras probaba esto, descubrí un error con deepmergeSi pasa un objeto vacío como primer argumento, deepmergedevolverá un objeto vacío. No estoy seguro de por qué.

const deepmerge = require('deep-merge')

const one = {}
const two = { nested: { value: 'two' } }
const three = deepmerge({}, one, two)

console.log(three) // {} ....... 🤷‍♂️

Desafortunadamente, ninguno de estos métodos admite la copia de descriptores de acceso.

const mergeOoptions = require('merge-options')

let count = 0
const one = {}
const two = {
  get count () { return count } ,
  set count (value) { count = value }
}
const three = mergeOptions(one, two)

console.log('two:' two)
console.log('three:', three)
Fusionar opciones no copia los accesos en el nuevo objeto.

Fusión profunda que incluye accesores

No pude encontrar una biblioteca que le permita realizar una fusión profunda mientras copia los accesos. No sé por qué la gente aún no lo ha creado 😢.

Así que seguí adelante y creé uno. Se llama mixAquí está el código para mezclar . (Explicaré cómo creé mixen el próximo artículo, ¡que debería ser divertido!).

Déjame decirte de lo que mixes capaz.

Dos características de mix

Primero, mixcopia los accesos.

let count = 0
const one = {}
const two = {
  get count () { return count },
  set count (value) { count = value }
}
const three = mix(one, two)

console.log('two:', two)
console.log('three:', three)
Mezclar copias de acceso.

En segundo lugar, mixcopia los objetos anidados y las matrices para que no tenga que preocuparse por la mutación.

const one = {}
const two = { nested: { value: 'two' } }
const three = mix(one, two)

// Nested values do not get mutated
three.nested.value = 'three'
console.log(two.nested.value) // 'two'

¡Eso es!

¡Te agradecería que mixsalgas a dar una vuelta y me avises si tienes algún comentario!

Publicar un comentario

0 Comentarios