Q:

Qual a diferença entre null e undefined?

A maioria das linguagens de programação possui um tipo "nulo" para representar a ausência de um valor. Ela pode possuir vários nomes (null, nil, None, etc), mas seu propósito é o mesmo (com exceção do SQL, onde NULL quer dizer "desconhecido"). JavaScript, entretanto, possui dois tipos distintos: null e undefined.
Qual a diferença entre esses dois tipos? Noto que nem sempre são usados consistentemente (embora em geral também signifiquem ausência de valor), de modo que fica difícil saber quando usar um ou outro. Sei que uma variável que nunca foi atribuída é undefined - bem como um campo não existente em um objeto - mas sei também que é possível atribuir manualmente esse valor através de var x = undefined; ou obj.foo = undefined.
Há alguma razão, na prática, para se usar um ou outro? Ou seria melhor usar somente um deles, e esquecer que o outro existe (e se sim, qual)? Misturar os dois no mesmo programa parece tornar mais difícil tratar casos de valor ausente... Qual seria a melhor abordagem no caso em que isso está fora do seu controle? (i.e. bibliotecas/frameworks externas usam um ou outro tipo no mesmo script)

A:

Diferença semântica
Como já apontado nas respostas do Calebe Oliveira e do Alexandre Marcondes, existe uma diferença semântica entre null e undefined: o valor null é utilizado para indicar a ausência de um objeto, enquanto undefined indica a ausência de um valor qualquer. A especificação explicita isso quando define os valores undefined e null:

valor undefined
valor primitivo utilizado quando uma variável não teve valor atribuído

valor null
valor primitivo que representa a ausência intencional de um valor de objeto

(Traduções livres)
O valor retornado pelo operador typeof é consistente com isso: ele retorna "object" para null, e "undefined" para undefined. O uso de null na especificação também obedece a isso. Por exemplo, todo objeto possui uma propriedade interna [[Prototype]], cujo valor deve ser um objeto ou null – nunca undefined. Como mencionado pelo Calebe, as APIs do DOM também fazem uso consistente disso. 
Diferenças sintáticas
Existem ainda diferenças sintáticas entre null e undefined. Ambos são os únicos valores de seus respectivos Tipos (Null e Undefined). Porém, null é também um termo reservado e um literal da gramática (como true, 0, "" e /.*/), o NullLiteral, enquanto undefined é exposto como uma propriedade do objeto global, assim como NaN e Infinity.
Além disso, undefined não é um termo reservado da linguagem – o que significa, por exemplo, que var undefined = "?" é uma construção válida. Portanto, é sempre possível criar uma variável local chamada undefined cujo valor não seja o primitivo de mesmo nome. Isso não é possível com null, var null = "?" gera um erro de sintaxe.  Em implementações antigas da linguagem também era possível sobrescrever o valor global undefined, mas isso deixou de ser permitido no ECMAScript 5, onde a propriedade undefined do objeto global é definida como [[Writable]]:false, o que significa que seu valor não pode ser substituído.
Na prática
As diferenças entre null e undefined são sutis, mas devem ser levadas em consideração pelos programadores; ignorá-las pode dar origem a bugs difíceis de identificar. Na prática, sempre leve em conta o seguinte:

Em variáveis, funções, propriedades e métodos nativos ou host (fornecidas pelo ambiente, como um browser) dos quais se espera um objeto como valor ou retorno, você irá encontrar null caso um objeto não esteja presente. É recomendável seguir essa convenção também em seu próprio código.
Variáveis inicializadas que nunca tiveram um valor atribuído contêm o valor undefined. Não se esqueça que declarações de variável são sempre erguidas (hoisted) para o topo do seu escopo, portanto:
foo === undefined; // true
var foo = 10;
foo === 10; // true

Parâmetros nomeados de função que não são passados no momento da chamada ganham o valor undefined:
(function(foo){
    foo === undefined; // true
}());

undefined é o valor retornado quando se tenta ler uma propriedade inexistente de um objeto:
var obj = {};
obj.foo === undefined; // true

null e undefined são valores falsey, ou seja, são convertidos para false quando forçados para booleanos:
if(null || undefined) { /* nunca executa */ }
!null === true; // true
!undefined === true; // true

Em comparações de igualdade abstrata (==) entre dois valores, null e undefined são considerados equivalentes:
null == undefined; // true
undefined == null; // true

Por isso, é comum comparar apenas com null para verificar equivalência com qualquer um dos dois:
if(x == null) { /* x pode ser null ou undefined */ }

Comparações de igualdade abstrata (==) entre null ou undefined e um valor de outro tipo sempre retornam false. Isso é válido inclusive para comparação com booleanos:
null == false; // false
undefined == false; // false

Ao encontrar uma referência a undefined em um trecho de código, tenha em mente que esse não é um termo reservado e pode ter sido redefinido:
(function(undefined){
    undefined === window.undefined; // false
}(10));

Quando um objeto é serializado com JSON.stringify, propriedades com valor undefined não são incluídas na saída (JSON não tem undefined). Portanto, se é importante ter a chave de uma propriedade na saída serializada, é melhor utilizar null para marcar que ela está vazia:
var o = {foo: undefined, bar: null };
JSON.stringify(o); // {"bar": null} 

Quando operações que implicam conversão de tipos são aplicadas em null e undefined, os resultados podem ser diferentes:
null + 1; // 1
undefined + 1; // NaN
null + ""; // "null"
undefined + ""; // "undefined"

A:

A principal diferença é que undefined é a ausência de algo na variável. Ele indica que uma variável nunca foi definida ou que alguém atribuiu undefined para limpar uma variável. Se você usar o typeof verá que o objeto indica ser do tipo "undefined".
var a;
console.log(typeof a);                      //resultado> "undefined"

console.log(typeof a === "object");         //resultado> false 
console.log(typeof a === "undefined");      //resultado> true
console.log(typeof a == "undefined");       //resultado> true

Já o nullé um valor nulo atribuído a um objeto. É como se você dissesse que é um objeto, mas que não foi inicializado ainda ou não foi criado. Ele é usado para passar valores padrão de objetos não inicializados. Se você usar o typeof verá que o objeto indica ser do tipo "object".
var a = null;
console.log(typeof a);                      //resultado> "object"

console.log(typeof a === "undefined");      //resultado> false
console.log(typeof a === null);             //resultado> true
console.log(typeof a == null);              //resultado> true

Na maioria das vezes você pode testar usando == tanto por undefined quanto por null que não vai fazer diferença, mas se você quer se assegurar se é algo que não foi atribuído ou se é um objeto vazio deve checar usando === pelo tipo específico. A comparação simples (==) compara apenas o valor e, se preciso, converte o valor para o tipo (caso de strings e números) enquanto a comparação estrita (===) compara o tipo e o valor, sem convertê-lo, se o tipo não for o mesmo ele retorna false. Veja como eles se comportam entre eles:
console.log(false == undefined);       //resultado> false
console.log(false == null);            //resultado> false
console.log(null == undefined);        //resultado> true
console.log(null === null);            //resultado> true
console.log(undefined === undefined);  //resultado> true
console.log(undefined === null);       //resultado> false
console.log(undefined == null);        //resultado> true

function test(val) {
    return val == null;
}
test(null);                            //resultado > true
test(undefined);                       //resultado > true

Você pode tirar vantagem dessa diferença quanto está verificando por parâmetros em funções e alguns deles são opcionais. Os parâmetros que não tiverem sido passados estarão com valor undefined e você pode aceitar um objeto vazio com null. Veja um exemplo:
function umaFuncao(primeiro, segundo, opcional) {
     if (typeof opcional === "undefined") {
        opcional = "três";
     }
    // faz algo
}

A:

De acordo com a documentação do ECMAScript, o valor undefined é utilizado quando uma variável não possui um valor. Já o null é utilizado quando se quer intencionalmente dizer que há uma ausência de qualquer valor de objeto para aquela variável.
Um exemplo prático é a chamada dos métodos ou variáveis como getElementById, childNodes[n], parentNode, etc., e eles retornarem null em algum momento. Isso significa que a propriedade chamada existe, mas não possui um valor, ou seja, não há um objeto a ela associado. Se algum método retornar undefined, indica que determinada propriedade não existe.
Demonstração:
var x;

x == null            // true
x == undefined       // true
x === null           // false
x === undefined      // true (x não tem valor!)

var y = null;

y == null            // true
y == undefined       // true
y === null           // true
y === undefined      // false (y existe, e seu valor é null)

typeof x             // 'undefined'
typeof y             // 'object'

Exemplos extraídos do SOen.

