JavaScript 等效于 jQuery 的扩展方法

IT技术 javascript jquery object extend
2021-02-06 15:10:48

背景

我有一个将config对象作为参数的函数。在函数中,我也有default对象。这些对象中的每一个都包含本质上用作函数内其余代码的设置的属性。为了避免必须在config对象中指定所有设置,我使用 jQuery 的extend方法来填充一个新对象,如果它们没有在对象中指定,则使用对象中的settings任何默认值defaultconfig

var config = {key1: value1};
var default = {key1: default1, key2: default2, key 3: default 3};

var settings = $.extend(default, config);

//resulting properties of settings:
settings = {key1: value1, key2: default2, key 3: default 3};

问题

这很好用,但我想在不需要 jQuery 的情况下重现此功能。是否有同样优雅(或接近)的方法来使用普通的 ol' javascript 来做到这一点?


编辑:非重复理由

此问题与“如何动态合并两个 JavaScript 对象的属性? ”问题不同。而这个问题只是想创建一个包含来自两个单独对象的所有键和值的对象 - 我特别想解决在两个对象共享一些但不是全部键以及哪个对象将获得优先级的情况下如何做到这一点(默认值)用于结果对象存在重复键的情况。更具体地说,我想解决使用 jQuery 的方法来实现这一点,并找到一种无需 jQuery 的替代方法。虽然这两个问题的许多答案重叠,但这并不意味着问题本身是相同的。

6个回答

要在您的代码中获得结果,您可以执行以下操作:

function extend(a, b){
    for(var key in b)
        if(b.hasOwnProperty(key))
            a[key] = b[key];
    return a;
}

请记住,您在那里使用扩展的方式将修改默认对象。如果您不想要那样,请使用

$.extend({}, default, config)

模仿 jQuery 功能的更强大的解决方案如下:

function extend(){
    for(var i=1; i<arguments.length; i++)
        for(var key in arguments[i])
            if(arguments[i].hasOwnProperty(key))
                arguments[0][key] = arguments[i][key];
    return arguments[0];
}
这不会递归,就像 $.extend 那样
2021-03-23 15:10:48
我想看到一个实际的代码示例,也许是小提琴,因为纯 js 非常酷但又很抽象,谢谢一百万。
2021-03-26 15:10:48

您可以在对象字面量中使用 ECMA 2018展开运算符...

var config = {key1: value1};
var default = {key1: default1, key2: default2, key 3: default 3};

var settings = {...default, ...config}

//resulting properties of settings:
settings = {key1: value1, key2: default2, key 3: default 3};

BabelJS 对旧浏览器的支持

如果它不执行深复制,则它不是扩展的通用替代品
2021-03-23 15:10:48
是否执行深复制?
2021-03-26 15:10:48
最整洁、最干净的方法!
2021-04-06 15:10:48

您可以使用 Object.assign。

var defaults = {key1: "default1", key2: "default2", key3: "defaults3"};
var config = {key1: "value1"};

var settings = Object.assign({}, defaults, config); // values in config override values in defaults
console.log(settings); // Object {key1: "value1", key2: "default2", key3: "defaults3"}

它将所有可枚举的自身属性的值从一个或多个源对象复制到目标对象并返回目标对象。

Object.assign(target, ...sources)

它适用于除 IE 之外的所有桌面浏览器(但包括 Edge)。它减轻了移动支持。

在此处亲自查看:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

关于深拷贝

但是,Object.assign 没有 jQuery 的扩展方法具有的深层选项。

注意:您通常可以使用 JSON 来达到类似的效果

var config = {key1: "value1"};
    var defaults = {key1: "default1", key2: "default2", keyDeep: {
        kd1: "default3",
        kd2: "default4"
    }};
    var settings = JSON.parse(JSON.stringify(Object.assign({}, defaults, config))); 
    console.log(settings.keyDeep); // Object {kd1: "default3", kd2: "default4"}
我想这正在发生。所以,它毕竟没有添加它。
2021-03-20 15:10:48
是的,但在这里它从默认值跳过 key1,而不是将它添加到新对象中。
2021-03-23 15:10:48
@Anonymous 实际上它确实添加了它,但是它被配置数组中的 key1 值覆盖了。
2021-03-28 15:10:48
“关于深拷贝”部分是错误的。第一期:parse( stringify( X ) ) === X. 第二个问题:“深”意味着其值为对象的键也将被递归合并。所以为了证明它,你应该使用类似的东西const a = { x: { a: 1 }, y: 2 }; const b = { x: { b: 2 }, y: 3 };并验证它deep(a, b) === { x: { a:1, b:2 }, y: 3 },这正是 jQuery deep 所做的。
2021-04-04 15:10:48

这是我在尝试消除 jQuery 依赖时提出的深度复制方法略有不同。它主要是为小巧而设计的,因此它可能没有人们期望的所有功能。应该完全兼容 ES5(由于使用Object.keys从 IE9 开始):

function extend(obj1, obj2) {
    var keys = Object.keys(obj2);
    for (var i = 0; i < keys.length; i += 1) {
      var val = obj2[keys[i]];
      obj1[keys[i]] = ['string', 'number', 'array', 'boolean'].indexOf(typeof val) === -1 ? extend(obj1[keys[i]] || {}, val) : val;
    }
    return obj1;
  }

你可能想知道第五行到底做了什么......如果 obj2.key 是一个对象字面量(即如果它不是普通类型)我们递归地调用它的扩展。如果 obj1 中尚不存在具有该名称的属性,我们首先将其初始化为一个空对象。否则,我们只需将 obj1.key 设置为 obj2.key。

以下是我的一些 mocha/chai 测试,它们应该可以证明这里的常见情况:

it('should extend a given flat object with another flat object', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: 42,
    prop3: true,
    prop4: 20.16,
  };
  const obj2 = {
    prop4: 77.123,
    propNew1: 'newVal1',
    propNew2: 71,
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'val1',
    prop2: 42,
    prop3: true,
    prop4: 77.123,
    propNew1: 'newVal1',
    propNew2: 71,
  });
});

it('should deep-extend a given flat object with a nested object', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: 'val2',
  };
  const obj2 = {
    propNew1: 'newVal1',
    propNew2: {
      propNewDeep1: 'newDeepVal1',
      propNewDeep2: 42,
      propNewDeep3: true,
      propNewDeep4: 20.16,
    },
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'val1',
    prop2: 'val2',
    propNew1: 'newVal1',
    propNew2: {
      propNewDeep1: 'newDeepVal1',
      propNewDeep2: 42,
      propNewDeep3: true,
      propNewDeep4: 20.16,
    },
  });
});

it('should deep-extend a given nested object with another nested object and deep-overwrite members', () => {
  const obj1 = {
    prop1: 'val1',
    prop2: {
      propDeep1: 'deepVal1',
      propDeep2: 42,
      propDeep3: true,
      propDeep4: {
        propDeeper1: 'deeperVal1',
        propDeeper2: 777,
        propDeeper3: 'I will survive',
      },
    },
    prop3: 'lone survivor',
  };
  const obj2 = {
    prop1: 'newVal1',
    prop2: {
      propDeep1: 'newDeepVal1',
      propDeep2: 84,
      propDeep3: false,
      propDeep4: {
        propDeeper1: 'newDeeperVal1',
        propDeeper2: 888,
      },
    },
  };
  assert.deepEqual(utils.extend(obj1, obj2), {
    prop1: 'newVal1',
    prop2: {
      propDeep1: 'newDeepVal1',
      propDeep2: 84,
      propDeep3: false,
      propDeep4: {
        propDeeper1: 'newDeeperVal1',
        propDeeper2: 888,
        propDeeper3: 'I will survive',
      },
    },
    prop3: 'lone survivor',
  });
});

我很高兴收到有关此实现的反馈或评论。提前致谢!

您可以使用for语句遍历对象的属性

var settings = extend(default, config);

function extend(a, b){
    var c = {};
    for(var p in a)
        c[p] = (b[p] == null) ? a[p] : b[p];
    return c;
}
当我阅读 jquery 基本代码时,它实际上执行了一个递归函数来复制或克隆值。所以它是深度克隆/复制,而不是像上面的代码那样在第一层复制。
2021-03-19 15:10:48
这不会考虑不存在于a. 虽然它不在示例中,但$.extend实际上是这样工作的,合并所有对象的所有属性。
2021-03-24 15:10:48