如何给对象设置默认值

369 阅读2分钟

1.用 ... 解构设置

在看compressor.js的时候,他用一个文件来存默认值,然后导出,最后用解构的方式,后面的值覆盖前面的方式设置默认值。当然也可以用Object.assign都是一样的

// defaults.js
export default {
    maxWidth: Infinity,
    maxHeight: Infinity,
    // ... 
}
​
// index.js
import DEFAULTS from './defaults';
​
export default class Compressor {
    constructor (option) {
        this.option = {
            ...DEFAULTS,
            ...option,
        }
    }
}

上面的方法看着挺方便的,但是还有缺陷,解决不了下面的问题

let user = {
  a: "a1",
  b: {
    c: "c1"
  }
}
​
let defaults = {
  a: "a2",
  b: {
    c: "c2",
    d: "d1" // 丢失了
  },
}
​
let option = {
  ...defaults,
  ...user
}
​
console.log(option) // { a: 'a1', b: { c: 'c1' } }

可以看到默认配置中 d丢失了,下面的方法可以解决这个问题

2.extend方法

Mescroll.js实现的方法,适合多个方法都需要默认值的时候,并且可以深度匹配默认值,默认值作为第二个参数传入,

MeScroll.extend = function (userOption, defaultOption) {
    if (!userOption) return defaultOption;
    for (var key in defaultOption) {
        if (userOption[key] == null) { // 如果用户没设置,则用默认值
            userOption[key] = defaultOption[key]; 
        } else if (typeof userOption[key] === 'object') {
            MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
        }
    }
    return userOption;
}

但是有些场景我们是需要有多个配置进行合并,我们可以参考jquery的extend实现方式

3.$.extend

$.extend([deep,] [target,] object1 [,objectN])

deep: Boolen类型,可选,表示是否进行递归合并(深/浅复制),为true是为深复制;默认值为false,浅复制。 target:扩展对象,可选,将接收新的属性。 objectN:一个对象,包含额外的属性,扩展到目标对象(扩展对象)。

我们可以砍掉一个没必要的功能,就是$.extend如果参数中只有一个对象,则扩展jquery对象。

// 此方法会改变原对象
jQuery.extend = jQuery.fn.extend = function () {
  var options, name, src, copy, copyIsArray, clone,
    target = arguments[0] || {},
    i = 1,    // i表示从第几个参数开始向目标参数进行合并,默认从第2个参数开始向第1个参数进行合并
    length = arguments.length,
    deep = false;  // 默认为浅度拷贝
  
  // 如果第一个参数是用来设置deep的boolean值,则将target设置为下一个参数
  if (typeof target === "boolean") {
    deep = target;
    
    target = arguments[i] || {};
    i++;
  }
  
  //  判断目标参数的类型,若目标参数既不是object类型,也不是function类型,则为目标参数重新赋值
  if (typeof target !== "object" && typeof target !== "function") {
    target = {};
  }
  
  // 从第i个参数开始
  for (; i < length; i++) {
    // 获取第i个不为null或undefined的参数
    if ((options = arguments[i]) != null) {
      
      for (name in options) {
        src = target[name];
        copy = options[name];
        
        // 防止循环引用
        if (name === "__proto__" || target === copy) {
          continue;
        }
        
        // 若deep为true,且当前参数中name字段的值存在且为object类型或Array类型,则进行深合并
        if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
          // 如果copy是数组类型,则判断目标参数中name字段的值是否存在,若存在则使用原来的,否则进行初始化
          if (copyIsArray && !Array.isArray(src)) {
            clone = [];
          } else if (!copyIsArray && !isPlainObject(src)) {
            clone = {};
          } else {
            clone = src;
          }
          copyIsArray = false;
          
          target[name] = merge(deep, clone, copy);
          
          // deep为false,表示浅拷贝,直接进行赋值
          // 或者copy不为Object对象、数组,则直接赋值
        } else if (copy !== undefined) {
          // 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性
          target[name] = copy;
        }
      }
    }
  }
  
  // 返回修改后的目标参数
  return target;
};

附1:jquery重构了的一段代码

if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
    if (copyIsArray) {
        copyIsArray = false;
        clone = src && Array.isArray(src) ? src : [];
    } else {
        clone = src && jQuery.isPlainObject(src) ? src : {};
    }
​
    target[name] = jQuery.extend(deep, clone, copy);
} else if ( copy !== undefined ) {
    target[name] = copy;
}
if (deep && copy && (isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
    if (copyIsArray && !Array.isArray(src)) {
        clone = [];
    } else if (!copyIsArray && !isPlainObject(src)) {
        clone = {};
    } else {
        clone = src;
    }
    copyIsArray = false;
​
    target[name] = merge(deep, clone, copy);
} else if (copy !== undefined) {
    target[name] = copy;
}

代码明显下面好看了许多,可以学习一下这种重构方式

附2:isEmptyObject、isPlainObject

1.isEmptyObject

isEmptyObject: function (obj) {
    var name;
​
    for (name in obj) {
        return false;
    }
    return true;
}
​
// 还可以利用JSON.stringify
JSON.stringify(obj) == "{}"

2.isPlainObject

function isPlainObject (target) {
    // 1.先排除明显不是Object的
    if (!target || Object.prototype.toString.call(target) !== "[object Object]") return false;
​
    // 2.判断是否有原型,没有原型的是简单对象
    const proto = Object.getPrototypeOf(target);
    if (!proto) return true;
​
    // 3.判断其构造函数是否为 Object
    const ctor = proto.constructor;
    return typeof ctor === "function" && ctor === Object;
}

参考

blog.csdn.net/weixin_3369…