Number of FIFO cache misses
JavaScript (ES6), 55 bytes
Method #1: the cache overwrites the input
Takes input in currying syntax (cache_size)(list).

n=&gt;a=&gt;a.map(x=&gt;a[a.indexOf(x,k&gt;n&amp;&amp;k-n)&lt;k||k++]=x,k=0)|k

Try it online!
How?
We overwrite the input array a[ ] with the cache, using a separate pointer k initialized to 0.
We use a.indexOf(x, k &gt; n &amp;&amp; k - n) &lt; k to test whether x is in the cache.
The cache can't grow faster than the original array is walked through, so each value is guaranteed to be found, either within or beyond the cache window (i.e. indexOf() will never return -1).
A value is in the cache if it is found at an index between max(0, k - n) and k - 1 (both bounds included), in which case we do a[true] = x. This only affects a property of the underlying object behind a[ ] but does not alter the array a[ ]. Otherwise, we do a[k++] = x.
Example
Below are the different steps for the input [1, 1, 2, 3, 3, 2, 1, 4] with a cache size of 2:

bold borders: map() pointer
brackets: cache pointer k
orange: current cache window
yellow: expired cache values



JavaScript (ES6), 57 bytes
Method #2: the cache is appended at the end of the input
Takes input in currying syntax (cache_size)(list).
n=&gt;a=&gt;a.map(x=&gt;n*~a.indexOf(~x,-n)||a.push(~x)&amp;k++,k=0)|k

Try it online!
How?
Because the input array a[ ] is guaranteed to contain non-negative integers, we can safely append the cache at the end of a[ ] by using the one-complement ~x of each value x.
We use n * ~a.indexOf(~x, -n) to test whether ~x is found among the last n values. Whenever this test fails, we append ~x to a[ ] and increment the number of misses k.
Example
Below are the different steps for the same example as above, using this method. Because cache values are simply appended at the end of the array, there is no explicit cache pointer.