When adding your event listener, you are creating a closure. Each event handler has the variable i in their scope. Unfortunately, it's the same variable i, the one that's incremented by your for loop. 
At the end of the loop, each event handler has, in its scope, a variable i whose value is now 6. It results in li[i] being undefined when the event is fired. 
You can solve this by, either : 
- not using iin your handler, for instance by using the providedeventvariable, that references the event. And thourh it, retrieves the targeted element :
var li = document.querySelectorAll("li");
for (var i = 0; i < li.length; i++) {
  li[i].addEventListener("click", function(event) {
    event.target.classList.toggle("done");
  })
}
.done {
  text-decoration: line-through;
}
<h1>Shopping List</h1>
<ul style="list-style-type:square">
    <li>Pencil</li>
    <li>Paper</li>
    <li>Eraser</li>
    <li>Table Lamp</li>
    <li>Comb</li>
</ul>
 
 
- declaring iwithletinstead ofvarin your for loop. This will result in each of your closure to have a different binding withi:
var li = document.querySelectorAll("li");
for (let i = 0; i < li.length; i++) {
  li[i].addEventListener("click", function() {
    li[i].classList.toggle("done");
  })
}
.done {
  text-decoration: line-through;
}
<h1>Shopping List</h1>
<ul style="list-style-type:square">
    <li>Pencil</li>
    <li>Paper</li>
    <li>Eraser</li>
    <li>Table Lamp</li>
    <li>Comb</li>
</ul>
 
 
- attaching the event listener to the UL. This would be my favorite : 
var ul = document.querySelector("ul");
ul.addEventListener("click", function(event) {
    event.target.classList.toggle("done");
  }, true);
.done {
  text-decoration: line-through;
}
<h1>Shopping List</h1>
<ul style="list-style-type:square">
    <li>Pencil</li>
    <li>Paper</li>
    <li>Eraser</li>
    <li>Table Lamp</li>
    <li>Comb</li>
</ul>
 
 
Read more about JavaScript closures.