JavaScript 减少和解构问题

JavaScript reduce and destructuring question

我目前正在学习 James Moore 的面向初学者的函数式编程 JavaScript Udemy 课程。我在理解一段代码的工作原理时遇到了一些困难:

const grades = [22, 77, 89, 90, 45, 77, 87, 92, 66, 44, 74, 81, 82, 81, 97];

const gradeCount = grades.reduce(computer, {});

function computer(acc, grade){
  const {a = 0, b = 0, c = 0, d = 0, f = 0} = acc;
  if (grade >= 90){
      return {...acc, a: a + 1};
  } else if (grade >= 80){
      return {...acc, b: b +1};
  } else if (grade >= 70){
      return {...acc, c: c + 1};
  }else if (grade >= 60){
      return {...acc, d: d + 1};
  } else { 
      return {...acc, f: f + 1};
  }
}

console.log(gradeCount);

  1. 我的第一个问题是;为什么这里使用解构而不是 const a=0, b=0, c=0, d=0, f=0;?与原始技术相比,这似乎没有那么冗长?

  2. 其次,为什么 reduce 方法 returns 一个包含所有等级及其相应数量的对象,而不是每个等级一个单独的对象?

提前感谢您的任何建议。

My first question is; why is destructuring used here as opposed to const a=0, b=0, c=0, d=0, f=0;? This seems far less verbose when compared to the original technique?

所以通常的变量赋值方式是这样的。

const obj = {
  a: 'A',
  b: 'B',
  c: 'C',
  d: 'D',
  e: 'E'
}

let a = obj.a || 0,
  b = obj.b || 0,
  c = obj.c || 0,
  d = obj.d || 0,
  e = obj.e || 0;

你看到这是多少代码了吗?让我们使用解构来实现相同的目的。

let {a = 0, b = 0, c = 0, d = 0, e = 0} = obj;

这要好得多并且做同样的事情。它正在定义来自 a to e 的变量并设置 default value to 0.

如果您必须从对象中获取嵌套值,那么 destructuring 会更容易。

const user = {
  name: {
    firstName: 'John',
    lastName: 'Doe'
  },
  age: '26'
}

let {name: {firstName, lastName}, age} = user;

让我们看看如何在函数参数中使用解构。所以我们有一个 user 对象,它是用户的 returns fullName。如果 firstNamelastName 为空,那么我们必须获取默认名称。

这是我们通常的做法:

function getUserFullName(user) {
  let firstName = user.firstName || 'Jane';
  let lastName = user.lastName || 'Doe';

  return firstName + ' ' + lastName;
}


const user = {
  name: {
    firstName: 'John',
    lastName: 'Doe'
  },
  age: '26'
}

getUserFullName(user);

使用解构的同样的事情可以这样完成:

function getUserFullName({name: {firstName = 'Jane', lastName = 'Doe'}} = user) {
  return `${firstName} ${lastName}`;
}

const user = {
  name: {
    firstName: 'John',
    lastName: 'Doe'
  },
  age: '26'
}

getUserFullName(user);

一开始可能会感到困惑,但一旦开始使用它,您就会意识到它的便利性。

Secondly, why is it that the reduce method returns one object containing all the grades with their corresponding quantities as opposed to a separate object for each grade?

这是因为computer方法returns一个包含所有成绩的对象。

return {...acc, a: a + 1};

要知道为什么你需要知道reduce方法是如何工作的。来自 MDN 文档:

The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value

所以每次调用 computer 方法时,accgrade 和 returns 都在最后一个值。

My first question is; why is destructuring used here as opposed to const a=0, b=0, c=0, d=0, f=0;? This seems far less verbose when compared to the original technique?

如果您按照建议声明变量,您将不会从对象中获取以前的值:

function original(obj) {
  const { a=0, b=0, c=0, d=0, f=0 } = obj;
  console.log(`const { a=0, b=0, c=0, d=0, f=0 } = obj;
a = ${a}
b = ${b}
c = ${c}
d = ${d}
f = ${f}
`);
}

function proposed(obj) {
  const a=0, b=0, c=0, d=0, f=0;
  
  console.log(`const a=0, b=0, c=0, d=0, f=0;
a = ${a}
b = ${b}
c = ${c}
d = ${d}
f = ${f}
`);
}

const obj = { a: 1, b: 2, d: 4};

original(obj);
proposed(obj);

解构将从 = 右侧的对象中取出 属性 a,并且只有在找不到它时才会分配零。所以,这类似于直接获取属性:

function explicit(obj) {
  const a = obj.a,
        b = obj.b, 
        c = obj.c, 
        d = obj.d, 
        f = obj.f;
  console.log(`const a = obj.a, b = obj.b, c = obj.c, d = obj.d, f = obj.f;
b = ${b}
c = ${c}
d = ${d}
f = ${f}
`);
}

const obj = { a: 1, b: 2, d: 4};

explicit(obj);

这并没有回退到零只是为了清楚发生了什么。后备值可以用 the conditional operator ? : 完成,看起来像这样:

const obj = {b: 2};

const a = obj.a ? obj.a : 0;
const b = obj.b ? obj.b : 0;

console.log(`Using the conditional operator "? :"
a = ${a}
b = ${b}
`)

或者,an idiomatic usage of the OR operator || 也可以产生后备值:

const obj = {b: 2};

const a = obj.a || 0;
const b = obj.b || 0;

console.log(`Using the OR operator "||"
a = ${a}
b = ${b}
`)

这些并不完全与在解构中提供默认值相同,但至少足以说明替代方案。不同之处在于如何处理虚假值,但我们现在可以忽略它。

因此,考虑到这一点,解构 比正常方式更简洁:

const a = obj.a || 0,
      b = obj.b || 0, 
      c = obj.c || 0, 
      d = obj.d || 0, 
      f = obj.f || 0;

//compared with

const { a=0, b=0, c=0, d=0, f=0 } = obj;

Secondly, why is it that the reduce method returns one object containing all the grades with their corresponding quantities as opposed to a separate object for each grade?

嗯,这就是 Array#reduce 的工作原理。我将简化一些事情并为简洁起见跳过不相关的细节 - 请随意阅读 MDN 文档,因为它比我在这里要全面得多。

reduce的形式为:

<array>.reduce(callback, initialValue)

你为它提供一个回调函数,该函数将使用两个参数为数组中的每个项目调用一次:

function callback(previousResult, currentItem){}
  1. 回调函数的上一个结果。除非这是第一次调用它,否则它将使用提供给 .reduceinitialValue
    • 注意 - previousResult 经常被命名为 prev for "previous" 或 acc for "accumulator"。为了清楚起见,我选择了长格式,但正如您在代码中看到的那样,它是 acc - 这是该参数的惯用名称。
  2. 当前正在操作的项目。项目将按顺序一一访问。

使用简单的 reduce 对数组中的所有项求和的快速说明:

callback = (acc, currentNumber) => acc + currentNumber;
initialValue = 0;
[1, 2, 3].reduce(callback, initialValue);

那么步骤 .reduce 将在此处说明:

                    [3,   5,   7]
                     ^    ^    ^
----------------------    |    |
acc =           0 |       |    |
currentNumber = 3 |       |    |
result =        3 |       |    |
-------------------       |    |
                          |    |
---------------------------    |
acc =           3 |            |
currentNumber = 5 |            |
result =        8 |            |
-------------------            |
                               |
--------------------------------
acc =           8 |
currentNumber = 7 |
result =        15|
-------------------

这同样适用于问题中的代码 - 每次回调只产生一个 单个 对象,所以下次它被调用时 acc 会得到一个又是一个对象。

最后,更新对象的方式。 {...acc} 将创建一个与前一个对象具有相同值的新对象,{...acc, a: a + 1} 将克隆它 并且 更改 属性 a 变量 a 的值加 1。如果之前没有 a 属性,那么它将被添加并且由于 variable a 将为零,然后你会得到 { a: 1 }.

const initial = { a: 5};

const obj1 = {...initial, a: 6};
console.log("obj1", obj1);

const obj2 = {...obj1, b: 1};
console.log("obj2", obj2);

const obj3 = {...obj2, b: 2};
console.log("obj3", obj3);