/proc/self is syntactic sugar. It's a shortcut to contatenating /proc/ and the result of the getpid() syscall (accessible in bash as the metavariable $$). It can get confusing, tho, in the case of shell scripting, as many of the statements invoke other processes, complete with the own PIDs... PIDs that refer to, more often than not, dead processes. Consider:
root@vps01:~# ls -l /proc/self/fd
lrwx------ 1 root root 64 Jan 1 01:51 0 -> /dev/pts/0
lrwx------ 1 root root 64 Jan 1 01:51 1 -> /dev/pts/0
lrwx------ 1 root root 64 Jan 1 01:51 2 -> /dev/pts/0
lr-x------ 1 root root 64 Jan 1 01:51 3 -> /proc/26562/fd
root@vps01:~# echo $$
'/bin/ls' will evaluate the path to the directory, resolving it as /proc/26563, since that's the PID of the process - the newly created /bin/ls process - that reads the contents of the directory. But by the time the next process in the pipeline, in the case of shell scripting, or by the time the prompt comes back, in the case of an interactive shell, the path no longer exists and the information output refers to a nonexistent process.
This only applies to external commands, however (ones that are actual executable program files, as opposed to being built into the shell itself). So, you'll get different results if you, say, use filename globbing to obtain a list of the contents of the directory, rather than passing the path name to the external process /bin/ls:
root@vps01:~# ls /proc/self/fd
0 1 2 3
root@vps01:~/specs# echo /proc/self/fd/*
/proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3
In the first line, the shell spawned a new process, '/bin/ls', via the exec() syscall, passing "/proc/self/fd" as argv. '/bin/ls', in turn, opened the directory /proc/self/fd and read, then printed, its contents as it iterated over them.
The second line, however, uses glob() behind the scenes to expand the list of filenames; these are passed as an array of strings to echo. (Usually implemented as an internal command, but there's often also a /bin/echo binary... but that part's actually irrelevant, since echo is only dealing with strings it never feeds to any syscall related to path names.)
Now, consider the following case:
root@vps01:~# cd /proc/self/fd
0 1 2 255
Here, the shell, the parent process of /bin/ls, has made a subdirectory of /proc/self its current directory. Thus, relative pathnames are evaluated from its perspective. My best guess is that this is related to the POSIX file semantics where you can create multiple hard links to a file, including any open file descriptors. So this time, /bin/ls behaves similarly to echo /proc/$$/fd/*.