JavaScript 等效于 jQuery 的扩展方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11197247/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 04:52:58  来源:igfitidea点击:

JavaScript equivalent of jQuery's extend method

javascriptjqueryobjectextend

提问by mbeasley

Background

背景

I have a function that takes a configobject as an argument. Within the function, I also have defaultobject. Each of those objects contains properties that essentially work as settings for the rest of the code within the function. In order to prevent having to specify all of the settings within the configobject, I use jQuery's extendmethod to fill in a new object, settingswith any default values from the defaultobject if they weren't specified in the configobject:

我有一个将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};

Problem

问题

This works great, but I'd like to reproduce this functionality without the need for jQuery. Is there an equally elegant (or close to) means to do this with plain ol' javascript?

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



Edit: Non-Duplicate Justification

编辑:非重复理由

This question is not a duplicate of the "How can I merge properties of two JavaScript objects dynamically?" question. Whereas that question simply wants to create an object that contains all of the keys and values from two separate objects - I specifically want to address how to do this in the event that both objects share some but not all keys and which object will get precedence (the default) for the resulting object in the event that there are duplicate keys. And even more specifically, I wanted to address the use of jQuery's method to achieve this and find an alternative way to do so without jQuery. While many of the answers to both questions overlap, that does not mean that the questions themselves are the same.

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

回答by Ryan Lynch

To get the result in your code, you would do:

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

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

Keep in mind that the way you used extend there will modify the default object. If you don't want that, use

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

$.extend({}, default, config)

A more robust solution that mimics jQuery's functionality would be as follows:

模仿 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];
}

回答by ling

You can use Object.assign.

您可以使用 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"}

It copies the values of all enumerable own properties from one or more source objects to a target object and returns the target object.

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

Object.assign(target, ...sources)

It works in all desktop browsers except IE (but including Edge). It has mitigated mobile support.

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

See for yourself here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

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

About deep copy

关于深拷贝

However, Object.assign does not have the deep option that jQuery's extend method have.

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

Note: you can generally use JSON for a similar effect though

注意:您通常可以使用 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"}

回答by Yaron

You can use the ECMA 2018 spread operator in object literals...

您可以在对象字面量中使用 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 support for older browsers

BabelJS 对旧浏览器的支持

回答by Rico Pfaus

This is my slightly different approach with deep copy I came up with while trying to eliminate a jQuery dependency. It is mostly designed for being small so it might have not all feature one expects. Should be fully ES5-compatible (starting from IE9 due to usage of Object.keys):

这是我在尝试消除 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;
  }

You may wonder what the fifth line does exactly do ... If obj2.key is an object literal (i.e. if it's no ordinary type) we recursively call extend on it. If a property with that name doesn't exist in obj1 yet, we initialize it to an empty object first. Otherwise we simply set obj1.key to obj2.key.

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

Here are some of my mocha/chai tests that should prove the common cases to work here:

以下是我的一些 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',
  });
});

I'd be happy about feedback or comments on this implementation. Thanks in advance!

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

回答by GeeWhizBang

I prefer this code that uses my generic forEachIn, and does not mangle the first object:

我更喜欢这段代码,它使用我的通用 forEachIn,并且不会破坏第一个对象:

function forEachIn(obj, fn) {
    var index = 0;
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            fn(obj[key], key, index++);
        }
    }
}

function extend() {
    var result = {};
    for (var i = 0; i < arguments.length; i++) {
        forEachIn(arguments[i],
            function(obj, key) {
                result[key] = obj;
            });
    }
    return result;
}

If you really do want to merge stuff into the first object, you can do:

如果你真的想将东西合并到第一个对象中,你可以这样做:

obj1 = extend(obj1, obj2);

回答by Ivan Kuckir

You can loop through Object's properties using forstatement.

您可以使用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;
}

回答by Alan Ktquez

It helps me a lot when I develop with pure javascript.

当我使用纯 javascript 进行开发时,它对我有很大帮助。

function extends(defaults, selfConfig){
     selfConfig = JSON.parse(JSON.stringify(defaults));
     for (var item in config) {
         if (config.hasOwnProperty(item)) {
             selfConfig[item] = config[item];
         }
     }
     return selfConfig;
}

回答by Doug Manuel

The approach of Ivan Kuckir'scan also be adapted to create a new object prototype:

Ivan Kuckir 的方法也可以用来创建一个新的对象原型:

Object.prototype.extend = function(b){
for(var key in b)
    if(b.hasOwnProperty(key))
        this[key] = b[key];
    return this;
}

var settings = default.extend(config);