Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript Object-Oriented JavaScript (2015) Constructor Functions and Prototypes Playlist Project

liktid
liktid
6,387 Points

From where are the currentSong.play() and currentSong.stop() methods coming from?

I just dont get where the the currentSong.play() and currentSong.stop() methods are coming from. We add methods "play()" and "stop()" to Playlist() in where we call play() and stop() ? What do i miss?

Thank you

3 Answers

liktid
liktid
6,387 Points

So play() and stop() are native methods? Well, it would be clever to mention that in the video. But thank you for your answer.

Andrew Chalkley
Andrew Chalkley
Treehouse Guest Teacher

Both the Playlist object that we created and the Song object have play() and stop() methods. These aren't native methods.

Because currentSong is a Song it has the stop() and play() methods for a song.

liktid
liktid
6,387 Points

Thank you for the clarification. But i think i've got a bug in my thinking of it somewhere.

When we look at the Playlist object we have:

Playlist.prototype.play = function() {
  var currentSong = this.songs[this.nowPlayingIndex];
  currentSong.play();
};

Playlist.prototype.stop = function(){
  var currentSong = this.songs[this.nowPlayingIndex];
  currentSong.stop();
};

My problem is now: How can we assign a method to the object when we already call the method in the method definition? (Example: Playlist.prototype.stop = function ... currentSong.stop();) I guess i'm just missing something :)

Andrew Chalkley
Andrew Chalkley
Treehouse Guest Teacher

Let's break down each line and see if this clears anything up.

//The Playlist is it's own object. It looks after the collection (an array) of songs. We could call this method stopPlaylist, but since it's on the Playlist it's unnecessary duplication.
Playlist.prototype.stop = function(){
  //this.songs is a collection of Song instances. We can get the currentSong (or currently playing song) from the array by using the nowPlayingIndex.
  var currentSong = this.songs[this.nowPlayingIndex];
  // Now we have the currentSong we can stop it. By calling it's stop() method. This is a different method on a different type of object. We could call this stopSong. Once again it's unnecessary duplication.
  currentSong.stop();
};

I've renamed them to show you that they are different methods here:

Playlist.prototype.stopPlaylist = function(){
  var currentSong = this.songs[this.nowPlayingIndex];
  currentSong.stopSong();
};

The main thing to get your head around is that the Playlist and Song are different objects. Imagine I had an interface that didn't have a playlist, and just played one thing, one Song. I would still need a play() and stop() method on a Song to control it. I've encapsulated everything the Song needs to know in the Song and everything in that the Playlist needs to in the Playlist. in this example it just so happens that the Playlist and Song have two methods named that same, but do slightly different things.

Does that clear it up?

liktid
liktid
6,387 Points

Yeah, nice. Thank you very much for the clarification. Your renaming example was exactly what i needed (and i finally realized and remembered that we didnt actually played music with it, i didnt get my head around how this should stop an audio file :))

Thank you for the awesome breakdown. Very kind of you.

Gabriel Ward
Gabriel Ward
20,222 Points

Hi Andrew,

I'm a bit confused by the play() and stop() methods in the .next prototype (or object? I'm not quite sure what it is exactly). Are the same methods that you use in the play and stop prototypes, like so

Javascript
currentSong.play();
currentSong.stop();

?

Andrew Chalkley
Andrew Chalkley
Treehouse Guest Teacher

Hey Gabriel Ward,

Here's the flow of the application:

  1. a song is playing in a playlist
  2. I press next
  3. the current song needs to stop playing, I don't want two songs playing at the same time
  4. the next song in the (array or) playlist. If you're at the end of the playlist it needs to loop back around to the beginning
  5. the next song, which is the new currentSong, needs to start playing

The this.stop() called the playlist's stop method. This is so we're not repeating any code that's already been written in the stop() method. Remember we need to stop the song so the next song isn't playing with the previous song. Then you need to check where the nowPlayingIndex is, is it after the end of the array or not? Then this.play() calls the play on the Playlist instance, just like we did for stop.

Does that clear it up anymore?

Regards Andrew

Gabriel Ward
Gabriel Ward
20,222 Points

Hi Andrew,

Yes that clears things up. I am a bit confused by where the definition of the behaviour of the stop() and play() methods in playlist.js comes from. In song.js we have defined the stop() and play() methods with the isPlaying = true/false boolean. But we haven't done anything like that for stop() and play() methods in playlist.js. They're simply called, and it's as if we've assumed that they'll magically stop/play the current song.

So I guess essentially my question is, are the .stop() and .play() methods in playlist.js and song.js the same? And if not, where are the .stop() and .play() methods in playlist.js getting their behaviour definition from?

Thank you for your help Andrew.

Daniel Sokol
Daniel Sokol
14,888 Points

Hi Gabriel, I know you asked this a long time ago and you probably have moved on, but maybe someone else has the same issues. I've been studying this project for several days after much confusion and struggling and I think it's starting to sink in... :) To help myself and others understand, here's how I see things:

The code in the .js files are divided into 2 groups: The objects and methods that set up the songs and playlist, and the methods that run the music player.

FIrst, the setup of the songs and playlist:

We create a constructor function named Song, which takes in 3 values: the title, artist and duration, and then sets a boolean isPlaying to false.

function Song(title, artist, duration) {
  this.title = title;
  this.artist = artist;
  this.duration = duration;
  this.isPlaying = false;
}

In the constructor, 'this.title' refers to the title of the song being sent to it, let's say in this case "Here Comes The Sun". The =title; part actually can be named anything. It can be named songtitle or titlename, but it makes sense to just call it title. We are essentially creating an object here for each song, and the object will use the term title inside of it. (e.g. hereComesTheSun{title: "Here Comes The Sun", etc.....}

Now, we can send some songs to the Song constructor function: we do this by creating a new variable for each song and using the keyword 'new' and calling the Song constructor function we just set up to send the new song info to it like so:

var hereComesTheSun = new Song("Here Comes the Sun", "The Beatles", "2:54");

We can do this for as many songs as we want, and each time, the Song constructor will create a new object for each song. The object for the above example is essentially this:

hereComesTheSun{title: "Here Comes The Sun", artist: "The Beatles", duration: "2:54"};

So after we do this for a few songs, we have a bunch of song objects floating around and we want to gather them all up. So we want to create an array of these objects.

In order to gather up the song objects into one array of objects (which basically will be the entire playlist of songs), we use a Playlist constructor function.

function Playlist() {
  this.songs = [];
  this.nowPlayingIndex = 0; 
}

But how do we send each song object to the Playlist array?

We create a Playlist.prototype.add function that pushes each song object to the array:

Playlist.prototype.add = function(song) {
  this.songs.push(song);
};

We call this function like so:

playlist.add(hereComesTheSun);

The word 'playlist' is not capitalized because we made that word into a variable that represents the Playlist constructor function for a bit of simplicity in calling that function.

var playlist = new Playlist();

So now, hereComesTheSun is pushed to the Playlist array, thereby adding it to the playlist.

We do the above steps over and over for each song you want to add to the playlist.

Then, we want to load the playlist songs to the HTML page, more specifically, to the id="playlist" area of the page. FIrst, we capture that area of the page in a new variable named "playlistElement" using document.getElementById("playlist"); .

var playlistElement = document.getElementById("playlist");

Then, we call the function playlist.renderInElement(playlistElement); to send it to the page.

playlist.renderInElement(playlistElement);

Below is the renderInElement function that we are calling:

Playlist.prototype.renderInElement = function(list) {
  list.innerHTML = "";
  for(var i = 0; i < this.songs.length; i++) {
    list.innerHTML += this.songs[i].toHTML(); //see toHTML function in song.js
  }
};

The renderInElement function first clears out the innerHTML of the id="playlist" area (which is represented here by the (list) argument), then it refills it with the songs in the playlist using a 'for' loop in order to loop through the array of objects we created in the steps above. But what this function is also doing is calling the Song.prototype.toHTML function, which transforms each song object into proper HTML format (<li> etc..) so it makes sense. The toHTML function adds: class="current" if this.isPlaying=true, but so far nothing is playing -every new Song has been set to isPlaying=false in the Song constructor function - so the class="current" is not added yet to any song. We're just loading the songs to the page right now and nothing more.

Finally, the page is loaded with the songs, and we can move to part 2 of what's happening:

After the page loads with songs nothing will happen until a button is clicked on. There are 3 buttons: Play, Next and Stop. Let's go through each of these separately:

PLAY: We capture the play button using document.getElementById("play"):

var playButton = document.getElementById("play");

Then, we use the event handler onClick to determine what happens when someone clicks the play button:

playButton.onclick = function() {
  playlist.play(); //see .play function in playlist.js file
  playlist.renderInElement(playlistElement); //see render function in playlist.js
}

So here's what's happening: First, we are calling the Playlist.prototype.play function:

Playlist.prototype.play = function() {
  var currentSong = this.songs[this.nowPlayingIndex];
  currentSong.play();
};

This function creates a new variable called currentSong. The 'currentSong' is based on what the nowPlayingindex happens to be at that time. Since nothing has played yet, the nowPlaylingIndex is still zero - it was set to zero at the beginning inside the Playlist constructor function. Remember, also in the Playlist constructor: this.songs=[] is the array of objects which represents the whole playlist. We want to target the currentSong to play so we do this by pointing to the song in the array of objects (the playlist) based on the nowPlayingIndex.

var currentSong = this.songs[this.nowPlayingIndex];

"this.songs" represents the array, and "this.nowPlayingIndex" is the current index which is currently [0]. So the currentSong is currently the first song in the array which is index [0].

Once the currentSong is established, the Playlist.prototype.play function calls the Song.prototype.play function. It's confusing to name both functions the same, but that's another issue. Here is the Song.prototype.play function:

Song.prototype.play = function() {
  this.isPlaying = true;
};

Here, we are simply setting the .isPlayling to 'true'. Why are we doing this? Well, if we go back the playButton.onclick event handler, there's a second part to that function that we haven't gotten to yet:

playlist.renderInElement(playlistElement);

This works the same way we described above when we were setting up the songs. We are first calling the Playlist.prototype.renderInElement function; which clears out the innerHTML of the playlistElement/list, then replaces it with the song list again using the 'for' loop and calling the 'Song.prototype.toHTML' function.

So why do we need to clear out the innerHTML only to replace it again with the same list? Because, now one of the songs (currentSong) is set to isPlaying=true. So the toHTML function will set the class for that song to class="current". Since the class is now 'current' for that song, the CSS for that song is different - it will be styled with a red background color.

Now, the 'NEXT' button:

We capture the button using document.getElementById("next"):

var nextButton = document.getElementById("next");

And use the onclick event handler to determine what happens when that button is pressed:

nextButton.onclick = function () {
  playlist.next();
  playlist.renderInElement(playlistElement);
}

First, we are calling the Playlist.prototype.next function:

Playlist.prototype.next = function() {
  this.stop();
  this.nowPlayingIndex++;
  if(this.nowPlayingIndex === this.songs.length) {
    this.nowPlayingIndex = 0;
  }
  this.play();
};

The 'next' function first calls the Playlist.prototype.stop function which captures the currentSong......:

Playlist.prototype.stop = function(){
  var currentSong = this.songs[this.nowPlayingIndex];
  currentSong.stop();
};

......then calls the Song.prototype.stop function which sets the currentSong to isPlaying=false.

Song.prototype.stop = function() {
  this.isPlaying = false;
};

Then the Playlist.prototype.next function advances the nowPlayingIndex by +1, and puts a condition in saying that if the nowPlayingIndex number is equal to the number of songs in the array of objects (the playlist), then set it back to zero.

Then the Playlist.prototype.play function is called using the current song (this.play), and the isPlaying setting is now 'true' for the next song.

The nextButton.onclick event handler then calls the .renderInElement function to reset the playlist:

playlist.renderInElement(playlistElement);

You can follow the steps above to see what happens when this is called. Remember, though, that the difference here is that the song that was playing before is now set to 'isPlaying=false' and the index was increased by one, so the next song in the array of objects (the playlist) is now considered the currentSong. When the playlist is once again sent through .renderInElement and .toHTML, the song that was playing before will lose the 'class="current"' (because isPlaying="false" for that song) and the next song will gain class="current" (because isPlaying is now set to 'true' for that song).

Now, the STOP button:

We capture the STOP button using document.getElementById("stop"):

var stopButton = document.getElementById("stop");

Then use the event handler onclick to determine what happens when it's pressed:

stopButton.onclick = function () {
  playlist.stop();
  playlist.renderInElement(playlistElement);
}

First, the Playlist.prototype.stop function is called:

Playlist.prototype.stop = function(){
  var currentSong = this.songs[this.nowPlayingIndex];
  currentSong.stop();
};

This captures the current song, and sends it to the Song.prototype.stop function:

Song.prototype.stop = function() {
  this.isPlaying = false;
};

It sets the current song to isPlaying=false.

The stopButton.onclick event handler then calls the .renderInElement function to reset the playlist:

playlist.renderInElement(playlistElement);

And we follow the steps above to reset the playlist on the page. But in this case, there are no songs set to isPlaying=true - every song is now set to false. So when the playlist is reloaded on to the page, nothing will be highlighted because none of the songs will have class="current".

Also, the index has not changed, so if the next event to occur on the page is a click of the play button, the last song that was playing will continue playing. If the next event on the page to occur is the next button, see the steps above for NEXT button - the index will increase by one (or reset to zero) and next song will begin playing.

I hope this explanation helps someone else as much as it helped me to write it. And if there are any mistakes, please don't hesitate to ask one of the experts around here :).

Good luck to all!

Thanks Daniel for your explanation. It is indeed confusing to give both play() methods the same name. This would work just the same and would be clearer. At least I think so.

      //from song.js
      Song.prototype.playSong= function() { 
          this.isPlaying = true
      }

      //from playlist.js
      Playlist.prototype.play= function() {
         var currentSong = this.songs[nowPlayingIndex]
         currentSong.playSong()
      }

I agree! Isabella Tan Hui Huang.. Take note @treehouse.