/**
 * @archivo obj.js
 * @módulo objeto
 * /

/ **
 * @callback obj:Cada devolución de llamada
 *
 * valor @param {Mixto}
 * La clave actual para el objeto que se está iterando.
 *
 * clave @param {cadena}
 * El valor-clave actual para el objeto que se está iterando
 * /

/ **
 * @callback obj:ReduceCallback
 *
 * @param {Mixto} accum
 * El valor que se acumula en el ciclo de reducción.
 *
 * valor @param {Mixto}
 * La clave actual para el objeto que se está iterando.
 *
 * clave @param {cadena}
 * El valor-clave actual para el objeto que se está iterando
 *
 * @return {Mixto}
 * El nuevo valor acumulado.
 * /
const toString = Objeto.prototipo.toString;

/ **
 * Obtener las llaves de un Objeto
 *
 * @param {Objeto}
 * El objeto del que obtener las llaves
 *
 * @return {cadena[]}
 * Una matriz de las claves del objeto. Devuelve una matriz vacía si el
 * el objeto pasado no era válido o no tenía claves.
 *
 * @privado
 * /
teclas const = función (objeto) {
  devolver isObject (objeto)? Objeto.claves(objeto) : [];
};

/ **
 * Iteración tipo matriz para objetos.
 *
 * @param {Objeto} objeto
 * El objeto a iterar sobre
 *
 * @param {obj: cada devolución de llamada} fn
 * La función de devolución de llamada que se llama para cada tecla en el objeto.
 * /
función de exportación cada uno (objeto, fn) {
  claves(objeto).forEach(clave => fn(objeto[clave], clave));
}

/ **
 * Reducción tipo matriz para objetos.
 *
 * @param {Objeto} objeto
 * El Objeto que desea reducir.
 *
 * @param {Función} fn
 * Una función de devolución de llamada que se llama para cada tecla en el objeto. Él
 * recibe el valor acumulado y el valor por iteración y clave
 * como argumentos.
 *
 * @param {Mixto} [inicial = 0]
 * Valor inicial
 *
 * @return {Mixto}
 * El valor final acumulado.
 * /
función de exportación reducir (objeto, fn, inicial = 0) {
  return claves(objeto).reduce((accum, clave) => fn(acum, objeto[clave], clave), inicial);
}

/ **
 * Object.assign-style object fusión/extensión superficial.
 *
 * @param {Objeto} objetivo
 * @param {Objeto} ...fuentes
 * @return {Objeto}
 * /
función de exportación asignar (objetivo, ...fuentes) {
  if (Objeto.asignar) {
    return Object.assign(objetivo, ...fuentes);
  }

  fuentes.forEach(fuente => {
    si (!fuente) {
      devolver;
    }

    each(fuente, (valor, clave) => {
      objetivo[clave] = valor;
    });
  });

  objetivo de retorno;
}

/ **
 * Devuelve si un valor es un objeto de cualquier tipo, incluidos los nodos DOM,
 * matrices, expresiones regulares, etc. Sin embargo, no funciona.
 *
 * Esto evita el problema cuando se usa `typeof` en un valor `null`
 * resulta en `'objeto'`.
 *
 * @param {Objeto} valor
 * @return {booleano}
 * /
función de exportación esObjeto(valor) {
  volver !!valor && tipo de valor === 'objeto';
}

/ **
 * Devuelve si un objeto parece ser un objeto "simple", es decir, un
 * instancia directa de `Objeto`.
 *
 * @param {Objeto} valor
 * @return {booleano}
 * /
función de exportación isPlain(valor) {
  devolver esObjeto(valor) &&
    toString.call(valor) === '[objeto Objeto]' &&
    valor.constructor === Objeto;
}