In addition to James Muspratt's excellent answer, I thought I might share some other tips that I have learned. In your page templates you can use conditionals to modify the behavior based on whether its an ajax request or not. In this way you can design your templates to work both as Ajax requests and as direct requests, which is great for SEO and other uses.
One problem for example with ajax loaded templates is that live preview may not work in the CP if the template is not also designed to load in the header, css, js, nav, etc. One way around this is to extend a different template based on the type of request:
{% extends craft.request.isAjax and not craft.request.isLivePreview ? "_ajax" : "_layout" %}
As you can see, if it's ajax I'm extending a template that has nothing but a content block defined; and it's not ajax then I'll extend the full template with head, js, css, nav, etc. The catch is that Live Preview also uses ajax, so we want the full template anyway.
You can also use the same method to return slightly modified content. In this case, I'm just loading the raw _product template for ajax requests and wrapping it as needed for non-ajax:
{% block content %}
{% if craft.request.isAjax and not craft.request.isLivePreview %}
{# return the raw _product template #}
{% include 'products/_product.html' %}
{% else %}
{# return the modified _product template wrapped properly for the full page layout #}
<section id="products" class="products js-products">
<div class="content">
{% include 'products/_product.html' %}
</div>
</section>
{% endif %}
{% endblock %}
Hope this helps a bit, and doesn't make things more confusing.