In general, code has good locality of reference if the memory accesses it makes tend to be sequentially located around a small number of areas of memory. For example, linear search over an array has great locality of reference because all the elements appear adjacent in memory, but linear search over a linked list has poor locality because the linked list cells don't necessarily appear consecutively in memory.
Let's take a look at quicksort. The "meat" of the quicksort algorithm is the partitioning step, where elements are rearranged around a pivot. There are several strategies for implementing the partition algorithm, most of which have excellent locality. One common approach works by scanning inward from the ends of the array toward the center, swapping elements whenever they're relatively out of place. This algorithm keeps most of the array accesses confined to two regions - the ends of the array - and accesses elements sequentially, so it has great locality.
Another partitioning strategy works by scanning from the left of the array to the right, storing two pointers delimiting the regions holding the smaller values and the greater values. Again, the array accesses are all sequential, so the locality is really good.
Now, contrast that with heapsort. In heapsort, the heap operations require you to repeatedly compare elements at one position with elements whose index is twice or half the index of that element. This means that the array accesses are scattered across the array rather than sequential, so the overall locality is a lot worse.
Mergesort actually has somewhat decent locality due to how the merge step works. However, because it maintains an auxiliary buffer array that's as large as the input array, it has to pay the cost of extra memory and its accesses are therefore a bit more scattered than quicksort's accesses.