Argumentos padrão de Javascript com escopo de bloco falham apenas no iOS

9

try {
  const val = 'correct value';
  (() => {
    ((arg = val) => {
      const val = 'ignored value';
      alert(arg);
    })();
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}

No OS X Chrome, no OS X Safari, no Android Chrome, no Windows Chrome, no Windows Firefox e até no Windows Edge, ele alerta "valor correto". No iOS Safari e iOS Chrome, ele alerta "Não é possível encontrar a variável: val".

Os seguintes snippets funcionam no iOS:

Não usando o argumento padrão (snippet 2):

try {
  const val = 'correct value';
  (() => {
    alert(val);
    (() => {
      const val = 'wrong value';
    })();
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}

Nenhuma função aninhada (trecho 3):

try {
  const val = 'correct value';
  ((arg = val) => {
    const val = 'ignored value';
    alert(val || 'wrong value');
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}

Variável não substituindo (trecho 4):

try {
  const val = 'correct value';
  (() => {
    ((arg = val) => {
      alert(arg);
    })();
  })();
} catch (err) {
  alert(err.message || 'Unknown error');
}

Bloqueie o escopo em vez da função (snippet 5):

try {
  const val = 'correct value';
  {
    ((arg = val) => {
      const val = 'ignored value';
      alert(arg);
    })();
  }
} catch (err) {
  alert(err.message || 'Unknown error');
}

Baseado no trecho 3, é claro que o valno arg = valdeve vir do escopo pai, não o escopo da função interior.

No primeiro trecho, o navegador não consegue encontrar valno escopo atual, mas, em vez de verificar os escopos ancestrais, usa o escopo filho, o que causa a zona morta temporal.

Isso é um bug do iOS ou estou entendendo mal o comportamento adequado do JS?

Esse erro está ocorrendo na saída do Webpack + Babel + Terser, portanto não podemos reescrever o código para evitar esse erro.

Leo Jiang
fonte

Respostas:

3

Eu acho que isso é uma conseqüência indesejada de uma implementação de buggy dos valores padrão do Param e seus TDZs . Suspeito que o iOS Safari ache que você está tentando atribuir algo que ainda não foi inicializado.

Para referência - o local do erro:

insira a descrição da imagem aqui


Solução alternativa 1 Não inicialize uma const de escopo interno com o mesmo nome que o parâmetro padrão e o escopo externo

try {
    const val = 'correct value';
    (() => {
        ((arg = val) => {
            const val_ = 'ignored value';       // <----
            alert(arg);
        })();
    })();
} catch (err) {
    console.error(err);
    console.error('msg', err.message || 'Unknown error');
}

Solução 2

Forçar consta let:

try {
    let val = 'correct value';                 // <----
    (() => {
        ((arg = val) => {
            const val = 'ignored value';
            alert(arg);
        })();
    })();
} catch (err) {
    console.error(err);
    console.error('msg', err.message || 'Unknown error');
}

Solução alternativa 3 Não reinicialize const valno fechamento mais interno:

try {
    const val = 'correct value';
    (() => {
        ((arg = val) => {
            // const val = 'ignored value';      // <--
            alert(arg);
        })();
    })();
} catch (err) {
    console.error(err);
    console.error('msg', err.message || 'Unknown error');
}
jzzfs
fonte