Como resolver?

O ponto chave nesse desafio é conseguir mapear todas as letras para então conseguirmos manipulá-las somando posições na ordem alfabética. Uma forma de fazer isso é criar uma "tabela" identificando cada letra, como um array ou um objeto, mas podemos também utilizar a já existente tabela ASCII.

Resolução

Para a resolução vamos utilizar a tabela ASCII para mapear as letras, pois o javascript possui métodos para lidar com isso out-of-the-box, facilitando nosso trabalho. Também vamos dividir a string em um array de caracteres com o .split() para manipulá-las melhor.

Feito isso precisamos inserir uma parte muito importante do nosso código. O número passado pode ter qualquer tamanho, mas o alfabeto é limitado, portanto precisamos que ao chegar na letra A ele volta para Z e vice-versa. Mas o nosso programa não precisa ficar dando muitas voltas pelas letras caso o número seja muito grande. Para isso podemos a matemática ao nosso favor. Com o resto da divisão por 26 (total de letras) conseguimos normalizar o número passado, deixando apenas o número exato de posições a mudar sem dar nenhuma "volta completa" por todas as letras.

function caesarCipher(str, key) {  const charsArray = str.split('')
  const normalizedKey = key % 26
}

Depois disso podemos iterar sobre o array de caracteres utilizando o .map() para obter os códigos de todos os caracteres atuais junto com o .charCodeAt() do próprio javascript. No caso de utilizar a tabela ASCII precisamos trabalhar com as posições de letras maiúsculas e minúsculas separadamente, pois elas ocupam posições separadas na tabela. Também devemos lidar com o caso de uma letra chegar ao começo do alfabeto (Z) e então precisar voltar ao fim (Z). Para isso podemos simplesmente somar 26 posições, pois ao subtrair será como se ela tivesse voltado para o fim do alfabeto.

function caesarCipher(str, key) {
  const charsArray = str.split('')
  const normalizedKey = key % 26

  const codesArray = charsArray.map(char => {
    const currentCode = char.charCodeAt(0)

    if (currentCode - normalizedKey < 65 && currentCode >= 65 && currentCode <= 90) {
      return currentCode + 26
    }

    if (currentCode - normalizedKey < 97 && currentCode >= 97 && currentCode <= 122) {
      return currentCode + 26
    }

    return currentCode
  })
}

Por fim agora só precisamos utilizar o String.fromCharCode do próprio javascript para obtermos a letra a partir do código atual subtraído da nossa chave normalizada. Utilizando o .map() faremos isso para todos as letras e depois apenas precisaremos unir o array em uma string.

function caesarCipher(str, key) {
  const charsArray = str.split('')
  const normalizedKey = key % 26

  const codesArray = charsArray.map(char => {
    const currentCode = char.charCodeAt(0)

    if (currentCode - normalizedKey < 65 && currentCode >= 65 && currentCode <= 90) {
      return currentCode + 26
    }

    if (currentCode - normalizedKey < 97 && currentCode >= 97 && currentCode <= 122) {
      return currentCode + 26
    }

    return currentCode
  })

  return codesArray.map(code => String.fromCharCode(code - normalizedKey)).join('')
}