ls lists the files and content of directories it is being passed as arguments, and if no argument is given, it lists the current directory. It can also be passed a number of options that affect its behaviour (see man ls for details).
If ls is being passed an argument called *, it will look for a file or directory called * in the current directory and list it just like any other. ls doesn't treat the * character in any other way than any other one.
However if ls * is a shell command line, then the shell will expand that * according to the corresponding shell's globbing (as referred to as Filename Generation or Filename Expansion) rules.
While different shells support different globbing operators, most of them agree on the simplest one *. * as a pattern means any number of characters, so * as a glob will expand to the list of files in the current directories that match that pattern. There's an exception however that a leading dot (.) character in a file name has to be matched explicitely, so * actually expands to the list of files and directories not starting with . (in alphabetical order).
For instance, if the current directory contains the files called ., .., .foo, -l and foo bar, * will be expanded by the shell to two arguments to pass to ls: -l and foo bar, so it will be as if you had typed:
ls -l "foo bar"
or
'ls' "-l" foo\ bar
Which are three ways to run exactly the same command. In all 3 cases, the ls command (which will probably be executed from /bin/ls from a lookup of directories mentioned in $PATH) will be passed those 3 arguments: "ls", "-l" and "foo bar".
Incidentally, in this case, ls will treat the first (strictly speaking second) one as an option.
Now, as I said, different shells have different globbing operators. A few decades ago, zsh introduced the **/ operator¹ which means to match any level of subdirectories, short for (*/)# and ***/ which is the same except that it follows symlinks while descending the directories.
A few years ago (July 2003, ksh93o+), ksh93 decided to copy that behaviour but decided to make it optional, and only covered the ** case (not ***). Also, while ** alone was not special in zsh (just meant the same as * like in other traditional shells since ** means any number of character followed by any number of characters), in ksh93, ** meant the same as **/* (so any file or directory below the current one (excluding hidden files). bash copied ksh93 a few years later (February 2009, bash 4.0), with the same syntax but an unfortunate difference: bash's ** is like zsh's ***, that is it follows symlinks when recursing into sub-directories which is generally not what you want it do and can have nasty side effects. The fish shell also supports ** the same way as bash with the same caveat. tcsh also added a globstar option in V6.17.01 (May 2010) and supports both ** and ***.
So in bash and ksh93, when the corresponding option is enabled (globstar), ** expands all the files and directories below the current one, and *** is the same as * though it's not impossible that future versions of those shells will adapt the zsh behaviour to also mean any file and directory below the current one, traversing the symbolic links.
Above, you'll have noticed the need to make sure none of the expansions is interpreted as an options. For that, you'd do:
ls -- *
Or:
ls ./*
There are some commands (it doesn't matter for ls) where the second is preferable since even with the -- some filenames may be treated specially. It's the case of - for most text utilities, cd and pushd and filenames that contain the = character for awk for instance. Prepending ./ to all the arguments removes their special meaning (at least for the cases mentioned above).
It should also be noted that most shells have a number of options that affect the globbing behaviour (like whether dot files are ignored or not, the sorting order, what to do if there's no match...), see also the $FIGNORE parameter in ksh
Also, in every shell but csh, tcsh, fish and zsh, if the globbing pattern doesn't match any file, the pattern is passed as an unexpanded argument which causes confusion and possibly bugs. For instance, if there's no non-hidden file in the current directory
ls *
Will actually call ls with the two arguments ls and *. And as there's no file at all, so none called * either, you'll see an error message from ls (not the shell) like: ls: cannot access *: No such file or directory, which has been known to make people think that it was ls that was actually expanding the globs.
The problem is even worse in cases like:
rm -- *.[ab]
If there's no *.a nor *.b file in the current directory, then you might end up deleting a file called *.[ab] by mistake (csh, tcsh, fish and zsh would report a no match error and wouldn't call rm).
If you do want to pass a literal * to ls, you have to quote that * character in some way as in ls \* or ls '*' or ls "*". In Bourne-like shells, globbing can disabled altogether using set -f.
¹ While (*/)# was always supported, it was first short-handed as ..../ in zsh-2.0 (and potentially before), then ****/ in 2.1 before getting its definitive form **/ in 2.2 (early 1992)