introduzir
Neste capítulo, explicaremos a estratégia de aprovar parâmetros para função funções no ECMAScript.
Na ciência da computação, essa estratégia é geralmente chamada de "estratégia de avaliação" (nota do tio: algumas pessoas dizem que ela é traduzida em estratégia de avaliação, enquanto outras a traduzem em estratégia de atribuição. Observando o conteúdo a seguir, acho mais apropriado chamar isso de estratégia de atribuição. De qualquer forma, o título deve ser escrito como uma estratégia de avaliação que é fácil para que todos entendam). Por exemplo, em linguagens de programação, definindo regras para expressões de avaliação ou cálculo. Uma estratégia para passar parâmetros para uma função é um caso especial.
http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/
O motivo para escrever este artigo é que alguém no fórum solicitou que explique com precisão algumas estratégias para a aprovação de parâmetros. Demos a definição correspondente aqui, esperando que seja útil para todos.
Muitos programadores estão convencidos de que, no JavaScript (mesmo em alguns outros idiomas), os objetos são passados por referência, enquanto o tipo de valor original é passado pelos valores. Além disso, muitos artigos falam sobre esse "fato", mas muitas pessoas realmente entendem esse termo e quantos estão corretos? Vamos explicar um a um neste artigo.
Teoria geral
Deve -se notar que, na teoria da atribuição, geralmente existem duas estratégias de atribuição: rigorosas - significa que os parâmetros são calculados antes de entrar no programa; Não estrito - significa que o cálculo dos parâmetros é calculado com base nos requisitos de cálculo (ou seja, é equivalente a atrasar o cálculo).
Então, aqui consideramos a estratégia básica de transferência de parâmetros da função, que é muito importante a partir do ponto de partida do ECMAScript. A primeira coisa a observar é que estratégias estritas de passagem de parâmetros são usadas no ECMAScript (mesmo em outros idiomas como C, Java, Python e Ruby).
Além disso, a ordem de cálculo dos parâmetros passados também é muito importante - no ECMAScript, ela é da esquerda para a direita e a ordem introdutória (faz da direita para a direita) implementada em outros idiomas também pode ser usada.
A estratégia estrita de transmissão também é dividida em várias estratégias de sementes, e discutiremos as estratégias mais importantes em detalhes neste capítulo.
Nem todas as estratégias discutidas abaixo são usadas no ECMAScript; portanto, ao discutir o comportamento específico dessas estratégias, usamos pseudo-código para mostrá-lo.
Passar por valor
Passando pelo valor, muitos desenvolvedores sabem que o valor do parâmetro é uma cópia do valor do objeto passado pelo chamador. Alterar o valor do parâmetro dentro da função não afetará o objeto externo (o valor do parâmetro externo). De um modo geral, a nova memória é alocada (não prestamos atenção em como a memória alocada é implementada - também é a pilha ou a alocação de memória dinâmica). O valor do novo bloco de memória é uma cópia do objeto externo e seu valor é usado dentro da função.
A cópia do código é a seguinte:
bar = 10
Procedimento Foo (Bararg):
bararg = 20;
fim
Foo (bar)
// Alterar o valor dentro do foo não afetará o valor da barra interna
Imprimir (barra) // 10
No entanto, se os parâmetros da função não forem o valor original, mas um objeto estrutural complexo, trará ótimos problemas de desempenho. C ++ tem esse problema. Ao passar a estrutura como um valor para a função, é uma cópia completa.
Vamos dar um exemplo geral, use a seguinte estratégia de atribuição para testá -la. Pense em uma função que aceita 2 parâmetros. O primeiro parâmetro é o valor do objeto e o segundo é uma marca booleana para marcar se o objeto é completamente modificado (reatribuindo o objeto para o objeto) ou apenas algumas propriedades do objeto são modificadas.
A cópia do código é a seguinte:
// NOTA: A seguir, são todos pseudo-código, não implementação JS
bar = {
x: 10,
Y: 20
}
Procedimento Foo (Bararg, IsfullChange):
Se IsfullChange:
bararg = {z: 1, q: 2}
saída
fim
bararg.x = 100
bararg.y = 200
fim
Foo (bar)
// Passar por valor, objetos externos não serão alterados
imprimir (barra) // {x: 10, y: 20}
// Altere completamente o objeto (atribua um novo valor)
Foo (bar, verdadeiro)
// também sem alteração
print (bar) // {x: 10, y: 20}, em vez de {z: 1, q: 2}
Passe por referência
Outra referência bem conhecida não é uma cópia de valor, mas uma referência implícita ao objeto, como o endereço de referência direto do objeto fora. Quaisquer alterações nos parâmetros dentro da função afetam o valor do objeto fora da função, porque ambos fazem referência ao mesmo objeto, ou seja, o parâmetro é equivalente a um alias do objeto externo.
Pseudocode:
A cópia do código é a seguinte:
Procedimento Foo (Bararg, IsfullChange):
Se IsfullChange:
bararg = {z: 1, q: 2}
saída
fim
bararg.x = 100
bararg.y = 200
fim
// Use o mesmo objeto que acima
bar = {
x: 10,
Y: 20
}
// O resultado da chamada por referência é a seguinte:
Foo (bar)
// O valor da propriedade do objeto foi alterado
Imprimir (barra) // {x: 100, y: 200}
// Reatipando novos valores também afeta o objeto
Foo (bar, verdadeiro)
// Este objeto já é um novo objeto
imprimir (barra) // {z: 1, q: 2}
Essa estratégia pode passar com mais eficiência objetos complexos, como grandes objetos de estrutura com grandes lotes de atributos.
Ligue para compartilhar
Todo mundo conhece as duas estratégias acima, mas você pode não conhecer a estratégia sobre a qual deseja falar aqui (na verdade, é uma estratégia acadêmica). No entanto, em breve veremos que essa é exatamente a estratégia que desempenha um papel fundamental na estratégia de entrega de parâmetros no ECMAScript.
Essa estratégia também possui alguns sinônimos: "Passe por objeto" ou "Passe por compartilhamento de objeto".
Essa estratégia foi proposta em 1974 por Barbara Liskov para a linguagem de programação da Clu.
O ponto -chave dessa estratégia é que a função recebe uma cópia (cópia) do objeto e a cópia de referência está associada aos parâmetros formais e seus valores.
Não podemos chamar as referências que aparecem aqui de "passe por referência", porque os parâmetros recebidos pela função não são alias de objeto direto, mas uma cópia do endereço de referência.
A diferença mais importante é que a função reatribua novos valores ao parâmetro interno não afetará o objeto externo (como o caso passado por referência no exemplo acima), mas como o parâmetro é uma cópia de endereço, o mesmo objeto acessado por fora e dentro (por exemplo, o objeto externo não é uma cópia completa, pois você deseja passar pelo valor). Alterar o valor do atributo do objeto Parâmetro afetará o objeto externo.
A cópia do código é a seguinte:
Procedimento Foo (Bararg, IsfullChange):
Se IsfullChange:
bararg = {z: 1, q: 2}
saída
fim
bararg.x = 100
bararg.y = 200
fim
// ainda usa esta estrutura de objeto
bar = {
x: 10,
Y: 20
}
// A passagem por contribuição afetará o objeto
Foo (bar)
// As propriedades do objeto foram modificadas
Imprimir (barra) // {x: 100, y: 200}
// A reatribuição não funciona
Foo (bar, verdadeiro)
// ainda é o valor acima
Imprimir (barra) // {x: 100, y: 200}
A suposição desse processamento é que os objetos usados na maioria dos idiomas não são os valores originais.
Passa por ação é um caso especial de aprovação por valor
A estratégia de entrega por compartilhamento é usada em vários idiomas: Java, Ecmascript, Python, Ruby, Visual Basic, etc. Além disso, a comunidade Python usou esse termo e pode ser usado em outros idiomas, pois outros nomes tendem a fazer as pessoas se sentirem confusas. Na maioria dos casos, como em Java, Ecmascript ou Visual Basic, essa estratégia também é chamada de valor por valor - significando: valor especial - cópia de referência (cópia).
Por um lado, é assim - os parâmetros passados para a função são apenas um nome do valor limitado (endereço de referência) e não afetarão o objeto externo.
Por outro lado, esses termos são realmente considerados como comer errados sem se aprofundar, pois muitos fóruns estão falando sobre como passar objetos para as funções JavaScript).
De fato, existe um ditado na teoria geral que passa pelo valor: mas neste momento esse valor é o que chamamos de cópia de endereço (cópia), para que não quebre as regras.
Em Ruby, essa estratégia é chamada PASS por referência. Deixe -me dizer novamente: não é passado em termos de uma cópia de uma grande estrutura (por exemplo, não por valor) e, por outro lado, não processamos referências ao objeto original e não podemos modificá -lo; Portanto, esse conceito de termos cruzados pode ser mais confuso.
Não há caso especial aprovado por referência em teoria como um caso especial aprovado pelo valor.
Mas ainda é necessário entender essas estratégias nas tecnologias mencionadas acima (Java, Ecmascript, Python, Ruby, outro). De fato, a estratégia que eles usam é passar compartilhando.
Pressione Compartilhar e Ponteiro
Para с/с ++, essa estratégia é a mesma que a passagem pelo valor do ponteiro, mas há uma diferença importante - essa estratégia pode desreferenciar os ponteiros e alterar completamente os objetos. No entanto, em geral, alocar um ponteiro de valor (endereço) para um novo bloco de memória (ou seja, o bloco de memória referenciado anteriormente permanece inalterado); Alterar as propriedades do objeto através de um ponteiro afetará o objeto externo de Adong.
Portanto, e categorias de ponteiro, podemos ver claramente que isso é passado pelo valor do endereço. Nesse caso, a passagem pelo compartilhamento é apenas "açúcar sintético", como o comportamento da atribuição de ponteiro (mas não a desreferência) ou modificando propriedades como referências (sem operação de desreferência necessária), às vezes pode ser denominada "ponteiro seguro".
No entanto, с/с + + também possui açúcar sintático especial ao se referir às propriedades do objeto sem desreferências de indicadores óbvios:
A cópia do código é a seguinte:
obj-> x em vez de (*obj) .x
Essa ideologia mais intimamente relacionada ao C ++ pode ser vista a partir da implementação de "ponteiros inteligentes". Por exemplo, no Boost :: shared_ptr, o operador de atribuição e o construtor de cópias estão sobrecarregados e o contador de referência do objeto também é usado para excluir o objeto através do GC. Esse tipo de dados ainda tem um nome semelhante - share_ptr.
Implementação do ECMAScript
Agora, conhecemos a política de aprovar objetos como parâmetros no ECMAScript - Pass by Share: Modificar as propriedades do parâmetro afetará o exterior, enquanto a reatribuição do valor não afetará o objeto externo. No entanto, como mencionamos acima, os desenvolvedores do ECMAScript geralmente chamam: Passe por valor, exceto que o valor é uma cópia do endereço de referência.
O inventor JavaScript Brendan Ashe também escreveu: O que é aprovado é uma cópia da referência (uma cópia do endereço). Então, o que todos mencionados no fórum disseram também está correto sob esta explicação.
Mais precisamente, esse comportamento pode ser entendido como uma tarefa simples. Podemos ver que os internos são objetos completamente diferentes, mas o mesmo valor é referenciado - ou seja, a cópia de endereço.
Código Ecmascript:
A cópia do código é a seguinte:
var foo = {x: 10, y: 20};
var bar = foo;
alerta (bar === foo); // verdadeiro
bar.x = 100;
bar.y = 200;
alerta ([foo.x, foo.y]); // [100, 200]
Ou seja, dois identificadores (ligação de nome) estão vinculados ao mesmo objeto na memória e compartilham este objeto:
valor foo: addr (0xff) => {x: 100, y: 200} (endereço 0xff) <= valor da barra: addr (0xff)
Para a reatribuição, a ligação é um novo identificador de objeto (novo endereço) sem afetar o objeto que foi previamente vinculado:
A cópia do código é a seguinte:
bar = {z: 1, q: 2};
alerta ([foo.x, foo.y]); // [100, 200] Sem mudança
alerta ([bar.z, bar.q]); // [1, 2] Mas agora a referência é um novo objeto
Ou seja, Foo e Bar agora têm valores diferentes e endereços diferentes:
A cópia do código é a seguinte:
valor foo: addr (0xff) => {x: 100, y: 200} (endereço 0xff)
Valor da barra: addr (0xfa) => {z: 1, q: 2} (endereço 0xfa)
Deixe -me enfatizar novamente que o valor do objeto mencionado aqui é o endereço, não a própria estrutura do objeto e atribuindo a variável a outra variável - uma referência ao valor atribuído. Portanto, as duas variáveis se referem ao mesmo endereço de memória. A próxima tarefa é o novo endereço, que é resolver a ligação ao endereço do objeto antigo e, em seguida, ligar ao endereço do novo objeto. Esta é a diferença mais importante entre a passagem por referência.
Além disso, se considerarmos apenas o nível de abstração fornecido pelo padrão ECMA-262, vemos apenas o conceito de "valor" no algoritmo, que implementa o "valor" aprovado (pode ser o valor original ou o objeto), mas de acordo com a nossa definição acima, ele também pode ser completamente chamado "aprovado pelo valor" porque o endereço de referência também é um valor.
No entanto, para evitar mal -entendidos (por que as propriedades de objetos externos podem ser alterados dentro da função), os detalhes do nível de implementação ainda precisam ser considerados aqui - o que vemos é passado compartilhando ou em outras palavras - passado por um ponteiro seguro, que não pode ser desreferenciado e alterado o objeto, mas o valor da propriedade do objeto pode ser modificado.
Versão de termo
Vamos definir o termo versão desta política no ECMAScript.
Pode ser chamado de "Pass by Value" - o valor mencionado aqui é um caso especial, ou seja, o valor é uma cópia de endereço. A partir deste nível, podemos dizer: Objetos no ECMAScript, exceto as exceções, são passados pelo valor, que na verdade é o nível de abstração do ECMAScript.
Ou, neste caso, é especificamente chamado de "passado por compartilhamento". Com isso, você pode ver a diferença entre a passagem tradicional por valor e a passagem por referência. Essa situação pode ser dividida em duas situações: 1: O valor original é passado pelo valor; 2: O objeto é passado compartilhando.
A frase "funcionar por tipos de referência" não tem nada a ver com o ECMAScript, e está errado.
para concluir
Espero que este post ajude a aprender mais detalhes no nível macro e implementação no ECMAScript. Como sempre, se houver alguma dúvida, não hesite em discutir.