beginPath, moveTo and lineTo

beginPath() empties the path. moveTo(x, y) starts a subpath, lineTo(x, y) draws a segment, and closePath() joins the end to the start with a proper corner.

BookNest's ratings as a line, a closed triangle, and a forgotten beginPath()HTML
<canvas id="c" style="width:600px; height:104px"></canvas><script src="hidpi.js"></script>
<script>
  const ctx = hidpi(c);
  ctx.lineWidth = 4;  ctx.strokeStyle = '#1f5f8b';  ctx.beginPath();
  const ratings = [4.6, 4.3, 4.8, 4.1, 4.5, 4.4];            // the six books, in order
  ratings.forEach((r, i) => ctx.lineTo(20 + i * 50, 100 - (r - 4) * 100));
  ctx.stroke();
  ctx.beginPath();  ctx.moveTo(320, 95);  ctx.lineTo(370, 10);  ctx.lineTo(420, 95);
  ctx.closePath();  ctx.fillStyle = '#e09a10';  ctx.fill();  ctx.stroke();
  ctx.moveTo(460, 95);  ctx.lineTo(580, 10);        // no beginPath() here...
  ctx.strokeStyle = '#b5452f';  ctx.stroke();       // ...so the triangle turns red too
</script>
Browser output of Listing 2.4
Browser output of 4

A lineTo() with no current point acts as moveTo(), so the loop needs no first step. The last stroke repaints every subpath since beginPath(), turning the blue triangle outline red; in an animation loop the same slip grows the path every frame.

Building paths with beginPath, moveTo, lineTo and closePath, and the missing-beginPath slipHTMLLive
<!doctype html>
<style>
  body { margin: 0; padding: 8px; font: 13px system-ui, sans-serif; background: #fafaf7; }
  canvas { width: 100%; max-width: 600px; height: 150px; display: block; background: #fff; border: 1px solid #ddd; }
  label { display: block; margin-top: 6px; }
</style>
<canvas id="c"></canvas>
<label><input type="checkbox" id="fix"> Call beginPath() before the last line</label>
<script>
  // Size the bitmap to the CSS size times devicePixelRatio for sharp lines
  function hidpi(c, r = devicePixelRatio) {
    c.width = c.clientWidth * r;  c.height = c.clientHeight * r;
    const ctx = c.getContext('2d');  ctx.scale(r, r);  return ctx;
  }
  const c = document.getElementById('c');
  const ctx = hidpi(c);
  const ratings = [4.6, 4.3, 4.8, 4.1, 4.5, 4.4];            // the six BookNest books

  function draw(fixed) {
    ctx.clearRect(0, 0, 600, 150);
    ctx.lineWidth = 4;  ctx.strokeStyle = '#1f5f8b';

    // 1. A rating line: lineTo() with no current point acts as moveTo()
    ctx.beginPath();
    ratings.forEach((r, i) => ctx.lineTo(20 + i * 50, 130 - (r - 4) * 130));
    ctx.stroke();

    // 2. A closed triangle: closePath() joins the end to the start with a real corner
    ctx.beginPath();
    ctx.moveTo(320, 130);  ctx.lineTo(370, 30);  ctx.lineTo(420, 130);
    ctx.closePath();
    ctx.fillStyle = '#e09a10';  ctx.fill();  ctx.stroke();

    // 3. A red line: without beginPath() the stroke repaints the triangle too
    if (fixed) ctx.beginPath();
    ctx.moveTo(460, 130);  ctx.lineTo(580, 30);
    ctx.strokeStyle = '#b5452f';  ctx.stroke();

    ctx.font = '12px sans-serif';  ctx.fillStyle = '#263238';
    ctx.fillText(fixed ? 'beginPath() first: only the line is red'
                       : 'no beginPath(): the triangle outline turns red too', 300, 146);
  }
  document.getElementById('fix').addEventListener('change', e => draw(e.target.checked));
  draw(false);
</script>