JavaScript 等效于 jQuery 的扩展方法

Posted

技术标签:

【中文标题】JavaScript 等效于 jQuery 的扩展方法【英文标题】:JavaScript equivalent of jQuery's extend method 【发布时间】:2012-06-27 04:12:48 【问题描述】:

背景

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

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 做到这一点?


编辑:非重复对正

此问题与“How can I merge properties of two JavaScript objects dynamically?”问题不重复。而这个问题只是想创建一个包含来自两个单独对象的所有键和值的对象 - 我特别想解决在两个对象共享一些但不是所有键并且哪个对象将获得优先权的情况下如何做到这一点(默认值)用于在存在重复键的情况下生成的对象。更具体地说,我想解决使用 jQuery 的方法来实现这一点,并找到一种不使用 jQuery 的替代方法。虽然这两个问题的许多答案重叠,但这并不意味着问题本身是相同的。

【问题讨论】:

盗用 jQuery 的做法 james.padolsey.com/jquery/#v=1.6.2&fn=jQuery.extend :-P 好主意@RocketHazmat,请注意,如果您复制该函数,它还有一些其他 jQuery 依赖项,例如 jQuery.isPlainObjectjQuery.isFunction @RocketHazmat 已经有人这样做了:gomakethings.com/vanilla-javascript-version-of-jquery-extend/… 【参考方案1】:

要在您的代码中获得结果,您可以:

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];

【讨论】:

我想看一个实际的代码示例,也许是小提琴,因为普通的 js 太酷了,但又太抽象了,谢谢一百万。 这不会像 $.extend 那样递归【参考方案2】:

您可以使用 ECMA 2018 spread operator in object literals...

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

【讨论】:

这是最整洁、最干净的方式! 是否执行深拷贝? 如果它不执行深拷贝,它不是扩展的通用替代【参考方案3】:

您可以使用 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 的 extend 方法所具有的 deep 选项。

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

【讨论】:

是的,但是这里它会从默认值跳过 key1,而不是将其添加到新对象中。 @Anonymous 实际上它确实添加了它,但它被配置数组中的 key1 值覆盖。 我想这正在发生。所以,它毕竟不是添加它。 “关于深拷贝”部分错误。第一期: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 所做的。【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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

【讨论】:

这不会考虑a 中不存在的属性。虽然不在示例中,但$.extend 实际上是这样工作的,合并所有对象的所有属性。 当我阅读 jquery 基本代码时,它实际上执行了一个递归函数来复制或克隆值。所以它是深度克隆/复制,而不仅仅是像上面的代码那样在第一级复制。【参考方案6】:

我更喜欢这段代码,它使用我的通用 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;

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

obj1 = extend(obj1, obj2);

【讨论】:

这是迄今为止最好的答案。它进行深度复制,不破坏第一个对象,支持无限对象,并且可以在扩展时更改类型(即这里的许多答案不会将字符串扩展到对象,这个答案会)。这得到了我书中的每一个复选标记。完全扩展替换,并且非常容易理解。【参考方案7】:

当我使用纯 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;

【讨论】:

【参考方案8】:

The approach of Ivan Kuckir's也可以适应创建新的对象原型:

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);

【讨论】:

以上是关于JavaScript 等效于 jQuery 的扩展方法的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript/jQuery 等效于 LINQ Any()

本机 javascript 等效于 jQuery :contains() 选择器

等效于 jQuery 中的 String.format

是否有等效的 Javascript 或 Jquery 睡眠功能?

jQuery event.preventDefault 的 Javascript 本机等效项 [重复]

Javascript/Coffeescript/jQuery 中是不是有与 Ruby 的发送等效的内容?