下划线:将对象数组转换为扁平化对象。这其中的奥妙是什么?

5
我将转换这个对象数组:

[  {    first: {      blah: 1,      baz: 2    }  },  {    second: {      foo: 1,      bar: 2    }  }]

转换成一个更简单、扁平的对象:
{
  first: {
    blah: 1,
    baz: 2
  },
  second: {
    foo: 1,
    bar: 2
  }
}

我发现使用 Underscore/LoDash 实现的两种最简单的方式如下:

// Using reduce and extend
_.reduce(myArray, _.extend)

// Using assign and apply
_.assign.apply(_, myArray);

完整的代码已经在JSBin上有文档记录:http://jsbin.com/kovuhu/1/edit?js,console 我已经阅读了许多关于apply/bind/call/assign/reduce/extend的文档,但我仍然无法理解幕后发生了什么。
有人可以帮助我理解这些一行代码背后的神奇技巧吗?

它们执行黑魔法,因为它们改变了你的对象中的第一个。正确的写法应该是 _.reduce(myArray, _.extend, {})(可以简化为 extend(extend({}, myArray[0]), myArray[1]))。 - Bergi
2个回答

4
为了理解这个概念,让我们来制作一个简单版本的 assign(别名为 extend)。我们只接受一个参数,我们将其称为源,然后将其添加到结果中。
function assign(result, source) {

  var keys = Object.keys(source),
    length = keys.length;

  for (var i=0 ; i < length; i ++) {
    var key = keys[i];
    result[key] = source[key];
  }

  return result;
}

现在让我们把输入分成几个部分:
var a = {
  first: {
    blah: 1,
    baz: 2
  }
}

var b = {
  second: {
    foo: 1,
    bar: 2
  }
}

var input = [a, b];

现在我们可以调用reduce函数:
_.reduce(input, assign);

reduce将调用两次分配(assign):

assign(result, a);
// assigns the (key, value) pair ("first", {blah: 1, baz: 2}) to result

assign(result, b);
// assigns the (key, value) pair ("second", {foo: 1, bar: 2}) to result

话虽如此,还需展示代码: http://jsbin.com/hexiza/3/edit?js,console

你可以在Lo-Dash源代码中看到如何使用createAssigner()和baseAssign()实现分配。


感谢Andrew展示了另一种做同样事情的方法(reduce + assign)。这个例子非常清晰。 - Kaelig

2
“reduce”函数接受一个数组中的所有值或者对象中的所有属性,并将它们汇总。在这个例子中,“reduce”传递了“extend”,这意味着你正在传递一个“function(a,b)”来扩展值“a”(每一步都会携带)与对象“b”的所有属性(依次为数组中的每个值)。在你的一行代码中,underscore的“reduce”遍历你的数组,从一个空对象{}开始,随着输入数组的遍历,简单地将所有数组元素的属性倒入该对象中。在普通的JS中,它可能是这样的:
var thing = {};
var deeparray = [{...}, {...}, {...}];
deeparray.forEach(function(v) {
  Object.keys(v).forEach(function(k) {
    thing[k] = v[k];
  }
}); 
return thing;

assign (在underscore中未找到) 做的事情与之相同,它接受一个对象数组并将它们全部合并在一起。这是它唯一要做的事情。调用 _assign.apply(_, myArray) 相当于调用 this.assign(myArray) - apply 是一个基本的 JavaScript 函数,用于在“手动覆盖”函数内部关键字 this 的含义时调用函数。_.assign.apply(_,myArray) 相当于调用 _.assing(myArray),但有一个额外的保证,即 this 将是 lodash,而不是某个随机的函数作用域上下文。


谢谢,我现在明白了reduceextend的作用。不确定为什么_.assign(myArray)不能工作,我们需要使用_来覆盖this。你能详细解释一下吗? - Kaelig
1
该函数在内部绑定于不同的作用域,因此如果您不使用 apply,则 "this" 将指向顶层函数作用域,根据我在源代码中所看到的。 - Mike 'Pomax' Kamermans

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接