Complementando, tem alguns detalhes que mudam conforme a linguagem.
Por exemplo, em Java e C#, os operadores lógicos só aceitam operandos booleanos. Já em outras linguagens, como Python, PHP e JavaScript, eles aceitam operandos de qualquer tipo.
Isso porque em Python, PHP e JavaScript existe o conceito de valores truthy e falsy, ou seja, qualquer valor pode ser convertido para true ou false se estiver em um contexto booleano.
Então valores como a string vazia, null/None e o número zero geralmente são considerados false, enquanto os demais valores são considerados true. Essas regras são definidas por cada linguagem e podem haver variações, como por exemplo a string "0", que em PHP é considerado false, enquanto que em Python e JavaScript é considerado true (nessas, somente o número zero é false, mas a string "0" não, porque não é vazia). Ou a lista vazia, que em Python é "falsa", mas em JavaScript um array vazio é considerado "verdadeiro".
Ou seja, algo como por exemplo if (a && b) é válido em JavaScript, independente do valor das variáveis a e b, mas em Java só seria válido se o tipo dessas variáveis fosse boolean.
Outro detalhe importante é que nem sempre o resultado do operador é um boolean. Em Python e JavaScript, por exemplo, o retorno sempre é um dos operandos. Exemplo:
# Em Python, operadores and e or retornam um dos operandos
a = 'abc'
b = 42
c = a and b
print(c) # 42
d = a or b
print(d) # abc
No caso do and, se o primeiro operando for verdadeiro (seja booleano, ou qualquer valor que seja correspondente a True), o resultado é o valor do segundo operando (caso contrário, retorna o valor do primeiro). E o or retorna o primeiro operando se este for verdadeiro, senão retorna o segundo.
Então quando fazemos if a and b: em Python, a expressão a and b retorna um dos valores de a ou b, e em seguida este é convertido para boolean para verificar se ele corresponde a "verdadeiro" ou "falso". Mas como podemos ver no exemplo acima, é possível usar o valor retornado diretamente, sem que este necessariamente faça parte de uma condição.
Por isso que é comum termos, por exemplo, coisas assim em JavaScript:
var x = valor1 || valor2 || valor3;
Ou seja, x recebe o primeiro dos valores que não for equivalente a false, ou o último, caso todos os anteriores sejam (lembrando que valores como o número zero, string vazia, null e undefined são considerados "falsos"). No caso, o código acima seria equivalente a:
var x;
if (valor1) {
x = valor1;
} else if (valor2) {
x = valor2;
} else {
x = valor3;
}
Já em outras linguagens (como Java, C# e PHP), o retorno destes operadores sempre é um boolean (ou seja, apenas true ou false).
E uma curiosidade: PHP tem tanto os operadores and/or quanto &&/||. A diferença entre eles é a precedência.
Vale lembrar também que esses operadores costumam ser short-circuit, ou seja, só avaliam o mínimo necessário. Por exemplo, a && b só é verdadeiro se ambos os operandos também o forem. Então se o primeiro é falso, ele nem avalia o segundo. Já a || b só é falso se ambos também o forem, então se o primeiro for verdadeiro, ele nem avalia o segundo.
Exemplo em JavaScript:
function a() {
console.log('chamando a()');
return false;
}
function b() {
console.log('chamando b()');
return true;
}
console.log('testando &&');
console.log(a() && b()); // a() retorna false, então nem chama b()
console.log('\ntestando ||');
console.log(b() || a()); // b() retorna true, então nem chama a()
A saída é:
testando &&
chamando a()
false
testando ||
chamando b()
true
Por fim, o XOR do JavaScript é apenas um operador bitwise, ou seja, ele trabalha com operandos numéricos e faz o XOR bit a bit dos seus valores. Por exemplo, se tivermos 14 ^ 9, o resultado é 7:
14 (00001110)
9 (00001001)
----------------
7 (00000111) <-- XOR bit a bit
É claro que se os números forem iguais (como 2 ^ 2) o resultado será 0, que equivale a false. Mas se os valores não forem números, como por exemplo 'a' ^ '', o resultado também é zero (se o operador fosse lógico, o resultado deveria ser equivalente a true, já que a string 'a' é considerada verdadeira, enquanto a string vazia é falsa).
No caso, para simular o XOR lógico em JavaScript, teria que fazer algo como if (Boolean(a) !== Boolean(b)) (ou if (!a !== !b), já que o uso do operador ! força a coerção para booleano).