introducir
En este capítulo, explicaremos la estrategia de aprobar los parámetros para funciones de funciones en ECMAScript.
En la informática, esta estrategia generalmente se llama "estrategia de evaluación" (nota del tío: algunas personas dicen que se traduce en una estrategia de evaluación, mientras que otras la traducen en una estrategia de asignación. Mirando el siguiente contenido, creo que es más apropiado llamarlo una estrategia de asignación. De todos modos, el título debe escribirse como una estrategia de evaluación que es fácil para que todos lo entiendan). Por ejemplo, en los lenguajes de programación, establecer reglas para la evaluación o las expresiones de cálculo. Una estrategia para transmitir parámetros a una función es un caso especial.
http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/
La razón para escribir este artículo es que alguien en el foro solicitó explicar con precisión algunas estrategias para aprobar los parámetros. Hemos dado la definición correspondiente aquí, con la esperanza de que sea útil para todos.
Muchos programadores están convencidos de que en JavaScript (incluso en algunos otros idiomas), los objetos se pasan por referencia, mientras que el tipo de valor original se pasa por valores. Además, muchos artículos hablan sobre este "hecho", pero muchas personas realmente entienden este término, ¿y cuántos son correctos? Lo explicaremos uno por uno en este artículo.
Teoría general
Cabe señalar que en la teoría de la asignación, generalmente hay dos estrategias de asignación: estricto, significa que los parámetros se calculan antes de ingresar al programa; Sin estrictamente: significa que el cálculo de los parámetros se calcula en función de los requisitos de cálculo (es decir, es equivalente a retrasar el cálculo).
Luego, aquí consideramos la estrategia de transferencia de parámetros de función básica, que es muy importante desde el punto de partida de ECMAScript. Lo primero a tener en cuenta es que las estrategias de aprobación de parámetros estrictos se utilizan en ECMAScript (incluso en otros idiomas como C, Java, Python y Ruby).
Además, el orden de cálculo de los parámetros aprobados también es muy importante: en ECMAScript, se deja a la derecha, y el orden introductorio (hace de derecha a la derecha) implementado en otros idiomas también se puede utilizar.
Las estrategias de transmisión estrictas también se dividen en varias estrategias de semillas, y discutiremos las estrategias más importantes en detalle en este capítulo.
No todas las estrategias que se analizan a continuación se utilizan en ECMAScript, por lo que al discutir el comportamiento específico de estas estrategias, utilizamos el pseudocódigo para mostrarlo.
Pasar por valor
Al pasar por valor, muchos desarrolladores son conscientes de que el valor del parámetro es una copia del valor del objeto aprobado por la persona que llama. Cambiar el valor del parámetro dentro de la función no afectará el objeto exterior (el valor del parámetro está afuera). En términos generales, se reactiva la nueva memoria (no prestamos atención a cómo se implementa la memoria asignada, también es la asignación de la pila o la memoria dinámica). El valor del nuevo bloque de memoria es una copia del objeto externo, y su valor se usa dentro de la función.
La copia del código es la siguiente:
barra = 10
Procedimiento Foo (BARARG):
bararg = 20;
fin
foo (bar)
// Cambiar el valor dentro de Foo no afectará el valor de la barra interna
imprimir (bar) // 10
Sin embargo, si los parámetros de la función no son el valor original, sino un objeto estructural complejo, traerá grandes problemas de rendimiento. C ++ tiene este problema. Al pasar la estructura como un valor en la función, es una copia completa.
Damos un ejemplo general, use la siguiente estrategia de asignación para probarla. Piense en una función que acepte 2 parámetros. El primer parámetro es el valor del objeto, y el segundo es una marca booleana para marcar si el objeto entrante está completamente modificado (reasignando el objeto al objeto) o solo se modifican algunas propiedades del objeto.
La copia del código es la siguiente:
// Nota: los siguientes son todos los pseudo-código, no implementación de JS
bar = {
X: 10,
Y: 20
}
procedimiento foo (bararg, isfullchange):
Si es un cambio de mule:
bararg = {z: 1, q: 2}
salida
fin
bararg.x = 100
bararg.y = 200
fin
foo (bar)
// Pase por valor, los objetos externos no se cambiarán
imprimir (bar) // {x: 10, y: 20}
// Cambiar completamente el objeto (asigne un nuevo valor)
foo (bar, verdadero)
// sin cambio tampoco
print (bar) // {x: 10, y: 20}, en lugar de {z: 1, q: 2}
Pasar por referencia
Otro bien conocido pasar por referencia no es una copia de valor, sino una referencia implícita al objeto, como la dirección de referencia directa del objeto exterior. Cualquier cambio en los parámetros dentro de la función afecta el valor del objeto fuera de la función, porque ambos hacen referencia al mismo objeto, es decir, el parámetro es equivalente a un alias del objeto externo.
Pseudocódigo:
La copia del código es la siguiente:
procedimiento foo (bararg, isfullchange):
Si es un cambio de mule:
bararg = {z: 1, q: 2}
salida
fin
bararg.x = 100
bararg.y = 200
fin
// usa el mismo objeto que el anterior
bar = {
X: 10,
Y: 20
}
// El resultado de la llamada por referencia es el siguiente:
foo (bar)
// El valor de la propiedad del objeto ha cambiado
imprimir (bar) // {x: 100, y: 200}
// Reasignar nuevos valores también afecta el objeto
foo (bar, verdadero)
// Este objeto ya es un objeto nuevo
imprimir (bar) // {z: 1, q: 2}
Esta estrategia puede pasar de manera más eficiente objetos complejos, como objetos de estructura grande con grandes lotes de atributos.
Llame compartiendo
Todos conocen las dos estrategias anteriores, pero es posible que no conozca la estrategia de la que desea hablar aquí (en realidad, es una estrategia académica). Sin embargo, pronto veremos que esta es exactamente la estrategia que juega un papel clave en la estrategia de entrega de parámetros en ECMAScript.
Esta estrategia también tiene algunos sinónimos: "pasar por objeto" o "pasar por el compartir objeto".
Esta estrategia fue propuesta en 1974 por Barbara Liskov para el lenguaje de programación CLU.
El punto clave de esta estrategia es que la función recibe una copia (copia) del objeto, y la copia de referencia está asociada con los parámetros formales y sus valores.
No podemos llamar a las referencias que aparecen aquí "pasar por referencia", porque los parámetros recibidos por la función no son alias de objeto directo, sino una copia de la dirección de referencia.
La diferencia más importante es que reasignar nuevos valores al parámetro dentro de la función no afectará el objeto externo (como en el caso aprobado por referencia en el ejemplo anterior), pero debido a que el parámetro es una copia de dirección, el mismo objeto accedido fuera y dentro es el mismo objeto (por ejemplo, el objeto externo no es una copia completa como desea pasar por el valor). Cambiar el valor de atributo del objeto de parámetro afectará al objeto externo.
La copia del código es la siguiente:
procedimiento foo (bararg, isfullchange):
Si es un cambio de mule:
bararg = {z: 1, q: 2}
salida
fin
bararg.x = 100
bararg.y = 200
fin
// todavía usa esta estructura de objeto
bar = {
X: 10,
Y: 20
}
// pasar por contribución afectará el objeto
foo (bar)
// Las propiedades del objeto se han modificado
imprimir (bar) // {x: 100, y: 200}
// La reasignación no funciona
foo (bar, verdadero)
// sigue siendo el valor anterior
imprimir (bar) // {x: 100, y: 200}
La suposición de este procesamiento es que los objetos utilizados en la mayoría de los idiomas no son los valores originales.
Pass by Share es un caso especial de pasar por valor
La estrategia de entrega al compartir se usa en muchos idiomas: Java, Ecmascript, Python, Ruby, Visual Basic, etc. Además, la comunidad de Python ha usado este término, y puede usarse en otros idiomas, ya que otros nombres tienden a hacer que las personas se sientan confundidas. En la mayoría de los casos, como en Java, Ecmascript o Visual Basic, esta estrategia también se llama pasar por valor - Significado: valor especial - copia de referencia (copia).
Por un lado, es así: los parámetros pasados a la función son solo un nombre del valor límite (dirección de referencia) y no afectarán el objeto externo.
Por otro lado, estos términos realmente se consideran que comen mal sin profundizar en él, ya que muchos foros están hablando sobre cómo pasar objetos a las funciones de JavaScript).
De hecho, hay un dicho en la teoría general que pasa por valor: pero en este momento este valor es lo que llamamos la copia de la dirección (copia), por lo que no rompe las reglas.
En Ruby, esta estrategia se llama pase por referencia. Permítanme decir nuevamente: no se pasa en términos de una copia de una estructura grande (por ejemplo, no por valor), y por otro lado, no procesamos referencias al objeto original y no podemos modificarla; Por lo tanto, este concepto de términos a través de términos puede ser más confuso.
No hay un caso especial aprobado por referencia en teoría como un caso especial aprobado por valor.
Pero todavía es necesario comprender estas estrategias en las tecnologías mencionadas anteriormente (Java, Ecmascript, Python, Ruby, Other). De hecho, la estrategia que usan es pasar al compartir.
Presione Share and Pointer
Para с/с ++, esta estrategia es la misma que pasar por el valor del puntero, pero hay una diferencia importante: esta estrategia puede desreferir punteros y cambiar por completo los objetos. Sin embargo, en general, la asignación de un puntero de valor (dirección) a un nuevo bloque de memoria (es decir, el bloque de memoria referenciado anteriormente permanece sin cambios); Cambiar las propiedades del objeto a través de un puntero afectará el objeto externo de Adong.
Entonces, y categorías de puntero, podemos ver claramente que esto se pasa por el valor de la dirección. En este caso, pasar por compartir es solo "azúcar sintética", como el comportamiento de asignación de puntero (pero no la desreferencia), o modificar propiedades como referencias (no se requiere operación de deserencia), a veces se puede nombrar "puntero seguro".
Sin embargo, с/с + + también tiene azúcar sintáctica especial cuando se refiere a propiedades del objeto sin dreferencias de punteros obvios:
La copia del código es la siguiente:
obj-> x en lugar de (*obj) .x
Esta ideología que está más estrechamente relacionada con C ++ se puede ver a partir de la implementación de "consejos inteligentes". Por ejemplo, en Boost :: shared_ptr, el operador de asignación y el constructor de copias están sobrecargados, y el contador de referencia del objeto también se usa para eliminar el objeto a través de GC. Este tipo de datos incluso tiene un nombre similar: Share_PTR.
Implementación de ECMAScript
Ahora sabemos que la política de aprobar objetos como parámetros en ECMAScript - Pase por compartir: Modificar las propiedades del parámetro afectará el exterior, mientras que reasignar el valor no afectará el objeto externo. Sin embargo, como mencionamos anteriormente, los desarrolladores de ECMAScript generalmente lo llaman: Pase por valor, excepto que el valor es una copia de la dirección de referencia.
El inventor de JavaScript, Brendan Ashe, también escribió: Lo que se pasa es una copia de la referencia (una copia de la dirección). Entonces, lo que todos mencionaron en el foro dijeron también es correcto bajo esta explicación.
Más precisamente, este comportamiento puede entenderse como una tarea simple. Podemos ver que las partes internas son objetos completamente diferentes, pero se hace referencia al mismo valor, es decir, la copia de la dirección.
Código ECMAScript:
La copia del código es la siguiente:
var foo = {x: 10, y: 20};
bar bar = foo;
alerta (bar === foo); // verdadero
bar.x = 100;
bar.y = 200;
alerta ([foo.x, foo.y]); // [100, 200]
Es decir, dos identificadores (vinculación de nombre) están unidos al mismo objeto en la memoria y comparten este objeto:
Valor de foo: addr (0xff) => {x: 100, y: 200} (dirección 0xff) <= valor de barra: addr (0xff)
Para la reasignación, el enlace es un nuevo identificador de objeto (nueva dirección) sin afectar el objeto que se ha unido previamente:
La copia del código es la siguiente:
barra = {z: 1, q: 2};
alerta ([foo.x, foo.y]); // [100, 200] sin cambios
alerta ([bar.z, bar.q]); // [1, 2] Pero ahora la referencia es un objeto nuevo
Es decir, Foo y Bar ahora tienen diferentes valores y diferentes direcciones:
La copia del código es la siguiente:
Valor de foo: addr (0xff) => {x: 100, y: 200} (dirección 0xff)
Valor de la barra: addr (0xfa) => {z: 1, q: 2} (dirección 0xfa)
Permítanme enfatizar nuevamente que el valor del objeto mencionado aquí es la dirección, no la estructura del objeto en sí, y asignando la variable a otra variable: una referencia al valor asignado. Por lo tanto, las dos variables se refieren a la misma dirección de memoria. La siguiente asignación es la nueva dirección, que es resolver el enlace a la dirección del objeto anterior y luego enlace a la dirección del nuevo objeto. Esta es la diferencia más importante entre pasar por referencia.
Además, si solo consideramos el nivel de abstracción proporcionado por el estándar ECMA-262, solo vemos el concepto de "valor" en el algoritmo, que implementa el "valor" aprobado (puede ser el valor original o el objeto), pero de acuerdo con nuestra definición anterior, también puede llamarse completamente "aprobado por el valor" porque la dirección de referencia también es un valor.
Sin embargo, para evitar malentendidos (por qué las propiedades de los objetos externos se pueden cambiar dentro de la función), los detalles del nivel de implementación deben considerarse aquí, lo que vemos se pasa al compartir, o en otras palabras, aprobados por un puntero seguro, que no puede ser derrotado y cambiado el objeto, pero el valor de la propiedad del objeto puede modificarse.
Versión de término
Definamos el término versión de esta política en ECMAScript.
Se puede llamar "Pase por valor": el valor mencionado aquí es un caso especial, es decir, el valor es una copia de dirección. Desde este nivel podemos decir: Los objetos en ECMAScript, excepto las excepciones, se pasan por valor, que en realidad es el nivel de abstracción de ECMAScript.
O en este caso, se llama específicamente "aprobado por compartir". A través de esto, puede ver la diferencia entre el paso tradicional por valor y el paso por referencia. Esta situación se puede dividir en dos situaciones: 1: El valor original se pasa por valor; 2: El objeto se pasa compartiendo.
La frase "funcionar haciendo referencia a los tipos" no tiene nada que ver con ECMAScript, y está mal.
en conclusión
Espero que esta publicación ayude a aprender más detalles a nivel macro e implementación en ECMAScript. Como siempre, si hay alguna pregunta, no dude en discutirlo.