A shell command (more precisely, a “simple command”) consists of a list of words. Each word can be an arbitrary string (a shell word can contain spaces and punctuation characters).
When you run echo -n *, the shell performs pathname expansion (also called filename generation or globbing) on *, and replaces it by the list of matching file names. So after expansion, this command consists of four words: echo, -n, f 1 and f 2. The command echo is run with two arguments, and it prints its arguments with a space in between (and no terminating newline because of the -n option). So the output is f 1 f 2. Exercise: create another file whose name consists of two consecutive spaces, run echo -n *, and make sure you understand the output.
When you run names=$(echo -n * ), the output from the command is stored in the names variable. Here, that line is equivalent to names='f 1 f 2'.
Now we get to array=( $names ). That's an array assignment, but it doesn't affect the expansion in this case. Since $names is an unquoted variable expansion, it's subject to word splitting followed by pathname expansion. Word splitting means that the value of the variable (which is a string) is split into pieces at each whitespace sequence (for the precise rules, search IFS in your shell's documentation). You can end up with zero, one or more words; here the string is split into 4 words: f, 1, f and 2. So the array contains four elements (each a one-character word). Exercise: with that extra file with two consecutive spaces in its name, what is now the exact contents of the array?
Next, you tried array=( * ). Here, there's a single word in the array, subject to the usual expansions, the last of which is pathname expansion. Since there are two matching files, the array contains two words, the names of each file: f 1 and f 2.
In terms of shell programming practice, what advice can we draw from this analysis? Well, first, there's the usual shell programming principle: always put double quotes around variable expansions, unless you have a good reason not to. Then, don't store a list in a string variable. If you want to store a list of file names, put it directly in an array:
files=(*)
ls -l "${files[@]}"
Further exercise: create a file whose name is a single asterisk (touch '*') and run these commands again. Do you understand the output?
Aside: zsh does not perform word splitting or pathname expansion on variable expansions. This makes it quite a bit saner to program in.