/**
 * 校验对象是否为空(布尔、函数、日期、正则默认算作非空)
 *
 * @param obj 对象
 * @returns {boolean}
 */
function isEmpty(obj) {
  try {
    if (obj === null || obj === undefined) {
      return true;
    }
    // 判断数字是否为 NaN
    if (typeof obj === "number") {
      return isNaN(obj);
    }
    // 判断参数是否是布尔、函数、日期、正则,是则直接返回 false
    if (typeof obj === "boolean" || typeof obj === "function" || obj instanceof Date || obj instanceof RegExp) {
      return false;
    }
    // 判断参数是否是字符串,去空,如果长度为0则返回true
    if (typeof obj === "string") {
      return obj.trim().length === 0;
    }
    if (typeof obj === 'object') {
      // 判断参数是否是数组,数组为空则返回true
      if (obj instanceof Array) {
        return obj.length === 0;
      }
      // 判断参数是否是对象,根据对象属性个数判断是否为空对象,是则返回
      if (obj instanceof Object) {
        return Object.getOwnPropertyNames(obj).length === 0;
      }
    }
    return true;
  } catch (e) {
    console.error(e);
    return false;
  }
}

/**
 * 根据键名获取对象的值
 *
 * @param obj 对象
 * @param key 键名(多层用[.]分隔)
 * @param defaultValue 为空返回默认值
 * @returns {undefined|*}
 */
function getDefaultValueByJsonKeys(obj, key, defaultValue = undefined) {
  // 校验数据是否有效,无效时返回默认值
  if ((isEmpty(key) || typeof key !== "string") && isEmpty(obj)) {
    return defaultValue;
  }
  // 用于存放最终从目标对象获取到的值
  let value = undefined;
  // 检查属于单层键还是多层键,只有一层时直接取值
  if (key.indexOf(".") <= -1) {
    value = obj[key.trim()];
  } else {
    // 存放临时对象(用于多层循环取值)
    let temp = obj;
    // 分割获取所有层级的键并过滤掉无效键
    const keys = key.trim().split('.').filter(item => !isEmpty(item));
    for (let i = 0, j = keys.length; i < j; i++) {
      if (typeof temp === 'object') {
        // 检查是否为最后一层,若不是则放到临时对象以继续循环
        if (i + 1 < j) {
          temp = temp[keys[i]];
        } else {
          value = temp[keys[i]]
        }
      } else {
        // 若没到最后一层且下一层已无值时返回默认值
        value = defaultValue;
        break;
      }
    }
  }
  // 验证值是否有效(布尔、函数、日期、正则默认算作非空),无效则返回默认值
  return isEmpty(value) ? defaultValue : value;
}