The last implementation just smooths out the changes - instead of using ‘Time.unscaledDeltaTime’ directly it moves towards it by 0.1 (10%) each time.
The unscaledDeltaTime might be unstable because some frame updates might take longer than others so it’s generally a good idea to smooth the FPS out like that (or by keeping a history buffer of previous values that you can average out).
So your first approach measures the “instantaneous” frame rate, based on the time to finish the last frame–which, as you discovered, it highly variable.
The other approaches you posted take the average framerate since the code started running. That’s probably OK if you have a single, consistent activity that you want to measure, but if conditions change over time (e.g. if you want to measure framerate while actually playing your game) that probably becomes useless pretty quickly.
To make a more generally-useful framerate display, you probably want to take the average framerate in a rolling window (e.g. over the last second, or the last 50 frames, or something like that), so that it’s more stable than the single-frame number but still changes over long periods of time as your game does different things. Naturally, this is more complicated than the other options. The simplest of several ways of doing that would probably be to use a ring buffer recording the time of the last N frames, and then average the contents of the buffer.