First, fsync() (and sync()) are POSIX-standard functions while syncfs() is Linux-only.
So availability is one big difference.
From the POSIX standard for fsync():
The fsync() function shall request that all data for the open file
  descriptor named by fildes is to be transferred to the storage
  device associated with the file described by fildes. The nature of
  the transfer is implementation-defined. The fsync() function shall
  not return until the system has completed that action or until an
  error is detected.
Note that it's just a request.
From the POSIX standard for sync():
The sync() function shall cause all information in memory that
  updates file systems to be scheduled for writing out to all file
  systems.
The writing, although scheduled, is not necessarily complete upon
  return from sync().
Again, that's not something guaranteed to happen.
The Linux man page for syncfs() (and sync()) states
sync() causes all pending modifications to filesystem metadata and
  cached file data to be written to the underlying filesystems.
syncfs() is like sync(), but synchronizes just the filesystem
  containing file referred to by the open file descriptor fd.
Note that when the function returns is unspecified.
The Linux man page for fsync() states:
fsync() transfers ("flushes") all modified in-core data of (i.e.,
  modified buffer cache pages for) the file referred to by the file
  descriptor fd to the disk device (or other permanent storage device)
  so that all changed information can be retrieved even if the system
  crashes or is rebooted.  This includes writing through or flushing a
  disk cache if present.  The call blocks until the device reports that
  the transfer has completed.
As well as flushing the file data, fsync() also flushes the metadata
  information associated with the file (see inode(7)).
Calling fsync() does not necessarily ensure that the entry in the
  directory containing the file has also reached disk.  For that an
  explicit fsync() on a file descriptor for the directory is also
  needed.
Note that the guarantees Linux provides for fsync() are much stronger than those provided for sync() or syncfs(), and by POSIX for both fsync() and sync().
In summary:
- POSIX 
fsync():  "please write data for this file to disk" 
- POSIX 
sync(): "write all data to disk when you get around to it" 
- Linux 
sync(): "write all data to disk (when you get around to it?)" 
- Linux 
syncfs(): "write all data for the filesystem associated with this file to disk (when you get around to it?)" 
- Linux 
fsync(): "write all data and metadata for this file to disk, and don't return until you do" 
Note that the Linux man page doesn't specify when sync() and syncfs() return.