Constructor function for persons with hobbies I'm trying to write a constructor function that generates instances of a user. One of the properties of the user class is an array of the user's hobbies. I'd like my constructor to have a method that generates a string representation of the array of hobbies provided to the constructor function. The goal is to create a grammatically correct sentence containing the hobbies of the Person instance, so if interests = ['hiking', 'biking', 'skiing'] the Person.bio() method will alert something like: This person's interests are: hiking, biking and skiing. I am trying to account for an array of unknown length being passed to the constructor.

I haven't used .reduce() much but from the little bit of testing I've done, this seems to do what I want it to do. I'm just looking for any critiques on how to make things more readable or performant!

Below is a stripped down version of the constructor function (missing things like name, age, etc. for clarity). 

function Person(interests = []) {
  this.interests = interests;
  this.hobbiesSentence = interests.reduce((hobbyString, hobby, index, interests) =&gt; {
    switch(index) {
      case (interests.length - 1):
        return hobbyString += `${hobby}.`
      case (interests.length - 2):
        return hobbyString += `${hobby} and `
      default:
        return hobbyString += `${hobby}, `
    }
  }, '');

  this.bio = function () {
    alert(`This person's interests are: ${this.hobbiesSentence}`)
  };
}


As I'm writing this, I could see a case for making the function exist but not necessarily creating/storing the sentence unless the bio() method is called. Any other ideas? Thanks in advance.
Performance

Strings are immutable, so using accumulator += stuffToAppend in a loop can traditionally impact performance. The problem is that we're creating a new string every iteration, leading to quadratic time complexity for an operation that should be linear. It turns out that modern browsers optimize this heavily using an internal array to represent string parts and make it quite fast over using an explicit array, so this post is focused on style rather than performance.

Design

On first thought, reduce seems like the right function from a semantic standpoint since we want to boil the array of interests down to one string. However, since avoiding string concatenation requires an intermediate array in reduce, we might as well just skip the intermediate array and use map and join. It's pretty common that reduce can be replaced with map or filter, which are more specific and succinct.

Switch statements are also generally not used much in JS (but often used in C...). You can replace many switch statements in JS with an object (particularly if you're choosing between a number of similar functions), or at least an if statement. Either way, the nature of the commas and "and" in this example makes it a bit awkward, so there doesn't seem to be any clear-cut win.

Additionally, this routine of "prettifying" a list is generic and can be moved to a separate function to keep Person clean.

As an aside, instead of switching between "interests", "hobbies" and "bio", it seems best to pick one term and stick with it throughout. 

Here's my attempt. This might seem a bit abstract, but it's typical in JS to avoid conditional/switch stuff as much as it is to avoid loops (which is the idea with reduce). If you prefer a more traditional approach, replace the joins array and indexing with an if statement and I'd still endorse it.



const prettyList = (a, sep=", ", endSep=[" and ", "."]) =&gt; 
  a.map((e, i) =&gt; e + (endSep[endSep.length-a.length+i] || sep)).join("")
;

const Person = function (interests=[]) {
  this.interests = interests;
  this.interestsSentence = prettyList(interests);

  this.interestsStr = () =&gt; 
    "This person's interests are: " + this.interestsSentence
  ;
};

const interests = ["foo", "bar", "baz", "quux"];

for (let i = 1; i &lt;= 4; i++) {
  console.log(new Person(interests.slice(0, i)).interestsStr());
}




Now the function is reusable and we can change its behavior without much effort at all:



const prettyList = (a, sep=", ", endSep=[" and ", "."]) =&gt; 
  a.map((e, i) =&gt; e + (endSep[endSep.length-a.length+i] || sep)).join("")
;

const activities = ["biking", "running", "walking", "skipping", "driving"];
console.log("I love", prettyList(activities, "; ", [", sometimes ", " but not ", "!"]));
console.log("I love", prettyList(activities, " and ", [" while ", " :-o"]));