Qual é o resultado do seguinte programa?
Copie o código do código da seguinte forma:
var foo = 1;
barra de função() {
se (!foo){
var foo = 10;
}
alerta(foo);
}
bar();
O resultado é 10;
E este?
Copie o código do código da seguinte forma:
var a = 1;
função b() {
uma = 10;
retornar;
função a() {}
}
b();
alerta(a);
O resultado é 1.
Isso te assusta? O que aconteceu? Isso pode ser estranho, perigoso e confuso, mas também é um recurso da linguagem JavaScript muito útil e impressionante. Não sei se existe um nome padrão para esse comportamento, mas gosto deste termo: "Içamento". Este artigo fornecerá uma explicação introdutória desse mecanismo, mas primeiro vamos ter o entendimento necessário do escopo do JavaScript.
Escopo do Javascript
Para iniciantes em Javascript, uma das áreas mais confusas é o escopo; na verdade, não é apenas para iniciantes; Conheci alguns programadores JavaScript experientes, mas eles não entendem profundamente o escopo. A razão pela qual o escopo do JavaScript é confuso é porque a própria sintaxe do programa se parece com uma linguagem da família C, como o seguinte programa C:
Copie o código do código da seguinte forma:
#include <stdio.h>
int principal() {
int x = 1;
printf("%d, ",x); //1
se (1) {
interno x = 2;
printf("%d, ", x); //2
}
printf("%d/n", x); //1
}
O resultado de saída é 1 2 1. Isso ocorre porque as linguagens da família C têm escopo de bloco. Quando o controle do programa entra em um bloco, como um bloco if, variáveis que afetam apenas o bloco podem ser declaradas sem afetar os efeitos fora do bloco. domínio. Mas em Javascript isso não funciona. Dê uma olhada no seguinte código:
Copie o código do código da seguinte forma:
var x = 1;
console.log(x);
se (verdadeiro) {
var x = 2;
console.log(x);
}
console.log(x);
O resultado será 1 2 2. Porque javascript é o escopo da função. Esta é a maior diferença da família de linguagens C. O if neste programa não cria um novo escopo.
Para muitos programadores C, C++ e Java, isso não é o que eles esperam e aceitam. Felizmente, devido à flexibilidade das funções JavaScript, existem maneiras de contornar isso. Se você precisar criar um escopo temporário, faça algo assim:
Copie o código do código da seguinte forma:
função foo() {
var x = 1;
se (x) {
(função () {
var x = 2;
// algum outro código
}());
}
// x ainda é 1.
}
Este método é flexível e pode ser usado em qualquer lugar onde você queira criar um escopo temporário. Não apenas dentro do quarteirão. No entanto, recomendo fortemente que você reserve um tempo para entender o escopo do JavaScript. É muito útil e um dos meus recursos favoritos do JavaScript. Se você entende o escopo, então a elevação variável fará mais sentido para você.
Declaração, nomenclatura e promoção de variáveis
Em JavaScript, existem 4 maneiras básicas de as variáveis entrarem no escopo:
•1 Linguagem incorporada: todos os escopos possuem este e argumentos (Nota do tradutor: Após o teste, os argumentos não são visíveis no escopo global);
•2 Parâmetros formais: Os parâmetros formais de uma função farão parte do escopo do corpo da função;
•3 Declaração de função: assim: function foo(){};
•4 Declaração de variáveis: assim: var foo;
Declarações de funções e declarações de variáveis são sempre "elevadas" silenciosamente ao topo do corpo do método pelo intérprete. Isso significa um código como o seguinte:
Copie o código do código da seguinte forma:
função foo() {
bar();
var x = 1;
}
será realmente interpretado como:
Copie o código do código da seguinte forma:
função foo() {
variável x;
bar();
x = 1;
}
Independentemente de o bloco no qual a variável está definida poder ser executado. As duas funções a seguir são na verdade a mesma coisa:
Copie o código do código da seguinte forma:
função foo() {
se (falso) {
var x = 1;
}
retornar;
var y = 1;
}
função foo() {
var x, y;
se (falso) {
x = 1;
}
retornar;
y = 1;
}
Observe que as atribuições de variáveis não são içadas, apenas declarações. No entanto, a declaração da função é um pouco diferente e o corpo da função também é promovido. Mas observe que existem duas maneiras de declarar uma função:
Copie o código do código da seguinte forma:
teste de função() {
foo(); // TypeError "foo não é uma função"
bar(); // "isso será executado!"
var foo = function () { // variável aponta para a expressão da função
alert("isso não será executado!");
}
function bar() { // Declaração de função function chamada bar
alert("isso será executado!");
}
}
teste();
Neste exemplo, apenas as declarações funcionais são içadas junto com o corpo da função. A declaração de foo será elevada, mas o corpo da função para o qual ela aponta só será atribuído durante a execução.
O texto acima cobre alguns dos princípios básicos do boost, e eles não parecem tão confusos. No entanto, em alguns cenários especiais, ainda existe um certo grau de complexidade.
Ordem de análise de variáveis
A coisa mais importante a ter em mente é a ordem de resolução variável. Lembra das 4 maneiras pelas quais a nomenclatura entra no escopo que dei anteriormente? A ordem em que as variáveis são analisadas é a ordem em que as listei.
Copie o código do código da seguinte forma:
<roteiro>
função a(){
}
var a;
alert(a); //Imprime o corpo da função de um
</script>
<roteiro>
var a;
função a(){
}
alert(a); //Imprime o corpo da função de um
</script>
//Mas preste atenção na diferença entre os dois métodos de escrita a seguir:
<roteiro>
var a=1;
função a(){
}
alert(a); //Imprime 1
</script>
<roteiro>
função a(){
}
var a=1;
alert(a); //Imprime 1
</script>
Existem 3 exceções aqui:
1 Os argumentos de nome integrados se comportam de maneira estranha. Parece que deveriam ser declarados após os parâmetros formais da função, mas antes da declaração da função. Isso significa que se houver argumentos no parâmetro formal, ele terá prioridade sobre o interno. Este é um recurso muito ruim, portanto evite usar argumentos em parâmetros formais;
2 Definir esta variável em qualquer lugar causará um erro de sintaxe, o que é um bom recurso;
3. Se vários parâmetros formais tiverem o mesmo nome, o último tem prioridade, mesmo que seu valor seja indefinido durante a operação real;
função nomeada
Você pode dar um nome a uma função. Nesse caso, não é uma declaração de função e o nome da função especificado (se houver, como spam abaixo, nota do tradutor) na definição do corpo da função não será promovido, mas ignorado. Aqui está um código para ajudá-lo a entender:
Copie o código do código da seguinte forma:
foo(); // TypeError "foo não é uma função"
barra(); // válido
baz(); // TypeError "baz não é uma função"
spam(); // ReferenceError "spam não está definido"
var foo = function () {}; // foo aponta para uma função anônima
barra de função() {}; // declaração da função
var baz = function spam() {}; // Função nomeada, apenas baz é promovido, spam não será promovido.
foo(); // válido
barra(); // válido
baz(); // válido
spam(); // ReferenceError "spam não está definido"
Como escrever código
Agora que você entende o escopo e o levantamento de variáveis, o que isso significa para a codificação JavaScript? O mais importante é sempre definir suas variáveis com var. E eu recomendo fortemente que, para um nome, sempre haja apenas uma declaração var em um escopo. Se você fizer isso, não terá problemas de escopo e elevação variável.
O que você quer dizer com especificação de linguagem?
Acho a documentação de referência do ECMAScript sempre útil. Aqui está o que descobri sobre escopo e elevação de variável:
Se uma variável for declarada em uma classe de corpo de função, ela será o escopo da função. Caso contrário, terá escopo global (como uma propriedade de global). Variáveis serão criadas quando a execução entrar no escopo. Os blocos não definirão novos escopos, apenas declarações de funções e procedimentos (o tradutor pensa que é uma execução global de código) criarão novos escopos. As variáveis são inicializadas como indefinidas quando são criadas. Se houver uma operação de atribuição na instrução de declaração de variável, a operação de atribuição só ocorrerá quando for executada, não quando for criada.
Espero que este artigo traga um raio de luz para os programadores que estão confusos sobre JavaScript. Também tento o meu melhor para evitar causar mais confusão. Se eu disse algo errado ou esqueci alguma coisa, por favor me avise.
Suplemento do tradutor
Um amigo me lembrou do problema de promoção de funções nomeadas no escopo global no IE:
Foi assim que testei quando traduzi o artigo:
Copie o código do código da seguinte forma:
<roteiro>
função(){
spam();
var baz = function spam() {alert('isto é spam')};
}
t();
</script>
Esta forma de escrever, ou seja, a promoção de funções nomeadas em escopo não global, tem o mesmo desempenho em ie e ff. Alterei para:
Copie o código do código da seguinte forma:
<roteiro>
spam();
var baz = function spam() {alert('isto é spam')};
</script>
Então o spam pode ser executado em ie, mas não em ff. Isso mostra que diferentes navegadores lidam com esse detalhe de maneira diferente.
Essa questão também me levou a pensar em outras duas questões 1: Para variáveis que possuem escopo global, há diferença entre var e não-var. Sem var, a variável não será promovida. Por exemplo, dos dois programas a seguir, o segundo reportará um erro:
Copie o código do código da seguinte forma:
<roteiro>
alerta(a);
var a=1;
</script>
Copie o código do código da seguinte forma:
<roteiro>
alerta(a);
uma=1;
</script>
2: Variáveis locais criadas em eval não serão promovidas (não tem como fazer isso).
Copie o código do código da seguinte forma:
<roteiro>
var a = 1;
função t(){
alerta(a);
avaliação('var a = 2');
alerta(a);
}
t();
alerta(a);
</script>