/**
 * @file obj.js
 * @模塊對象
 */

/**
 * @callback OBJ: 每個回呼
 *
 * @param {混合} 值
 * 正在迭代的對象的當前鍵。
 *
 * @param {string} 鍵
 * 正在迭代的對象的當前鍵值
 */

/**
 * @callback OBJ: 縮減全部回溯
 *
 * @param {混合}累積
 * 在 reduce 循環中累積的值。
 *
 * @param {混合} 值
 * 正在迭代的對象的當前鍵。
 *
 * @param {string} 鍵
 * 正在迭代的對象的當前鍵值
 *
 * @return {混合}
 * 新的累計值。
 */
const toString = Object.prototype.toString;

/**
 * 獲取對象的鍵
 *
 * @param {對象}
 * 從中獲取密鑰的對象
 *
 * @return {字符串[]}
 * 來自對象的鍵數組。如果
 * 傳入的對象無效或沒有鍵。
 *
 * @私人的
 */
常量鍵=函數(對象){
  返回對象(對象)?對象鍵(對象):[];
};

/**
 * 對象的類數組迭代。
 *
 * @param {Object} 對象
 * 要迭代的對象
 *
 * @param {物件:每個回呼} FN
 * 為對像中的每個鍵調用的回調函數。
 */
導出函數每個(對象,fn){
  keys(object).forEach(key => fn(object[key], key));
}

/**
 * 對象的類數組化簡。
 *
 * @param {Object} 對象
 * 您要減少的對象。
 *
 * @param {函數} fn
 * 為對像中的每個鍵調用的回調函數。它
 * 接收累計值和每次迭代的值和鍵
 * 作為參數。
 *
 * @param {混合} [初始= 0]
 * 初始值
 *
 * @return {混合}
 * 最終累計值。
 */
導出函數 reduce(object, fn, initial = 0) {
  return keys(object).reduce((accum, key) => fn(accum, object[key], key), initial);
}

/**
 * Object.assign 風格的對象淺合併/擴展。
 *
 * @param {Object} 目標
 * @param {物件}... 來源
 * @return {對象}
 */
導出函數分配(目標,...來源){
  如果(對象。分配){
    返回對象。分配(目標,... 源);
  }

  sources.forEach(來源=>{
    如果(!來源){
      返回;
    }

    每個(源,(值,鍵)=> {
      目標[鍵] = 值;
    });
  });

  返回目標;
}

/**
 * 返回值是否是任何類型的對象——包括 DOM 節點,
 * 數組、正則表達式等不過不是功能。
 *
 * 這避免了在 `null` 值上使用 `typeof` 的陷阱
 * 結果為“對象”。
 *
 * @param {Object} 值
 * @return {布爾值}
 */
導出函數 isObject(值) {
  返回!!值 && 值類型 === '對象';
}

/**
 * 返回一個對像是否看起來是一個“普通”對象——也就是說,一個
 * `Object` 的直接實例。
 *
 * @param {Object} 值
 * @return {布爾值}
 */
導出函數 isPlain(value) {
  返回 isObject(值) &&
    調用(值)=== '[對象對象]' &&
    value.constructor === 對象;
}