Java y JavaScript no están en la misma familia de lenguajes, y Java no fue una influencia importante en la semántica de JavaScript. Como otras personas señalaron, el nombre “JavaScript” fue una campaña de marketing de (en ese momento) Sun y Netscape [1]. A pesar de que JavaScript “se parece” a Java, y se comercializa como un complemento de Java, la mayoría de las influencias semánticas provienen de otras ramas de los idiomas: Self y Scheme son influencias pesadas.
Sin embargo, Java y JavaScript de esa época son casi completamente diferentes de Java y JavaScript que conocemos hoy. Así que me voy a centrar en el estado actual de ambos idiomas (Java 9 para Java y ECMAScript 2017 para JavaScript).
A medida que los idiomas evolucionan, tienden a incorporar características que se encuentran comúnmente en la investigación o son populares en otros idiomas, por lo que las cosas terminan convergiendo hasta cierto punto de todos modos, por lo que esta respuesta probablemente estará muy desactualizada en unos pocos años.
Las siguientes secciones enumerarán las diferencias (y similitudes, cuando corresponda) separadas por categorías, por lo que es más fácil comparar dónde difieren exactamente los dos idiomas.
SINTAXIS
Tanto Java como JavaScript tienen una sintaxis similar en varios lugares. Los comentarios, las palabras clave, los operadores, los delimitadores de bloque y algunas construcciones de procedimiento comunes (como for, while, if, etc.) son en su mayoría sintácticamente iguales. Esto no es sorprendente ya que ambos extraen de C aquí.
- Ambos lenguajes separan expresiones y declaraciones, por lo tanto, los procedimientos requieren una declaración explícita de ” retorno “;
- Ambos lenguajes tienen un argumento ” esto ” implícito en cada método, pero la forma en que se declaran estos métodos difiere.
En Java, los métodos solo pueden aparecer dentro de las declaraciones de clase y, por lo tanto, siempre se declaran de la siguiente manera:
( …) {
}
// P.ej:
clase F {
public void myMethod (int x, int y) {
devuelve esto. a + x + y;
}
}
En JavaScript, los métodos se pueden declarar dentro de una declaración de clase, dentro de un objeto literal o en el nivel de instrucción. Discutiremos por qué sucede esto cuando hablemos de las diferencias en los modelos de objetos más adelante.
// un método a nivel de declaración
función myMethod (x, y) {
devuelve esto. a + x + y;
}
// un método en una declaración de clase
clase F {
myMethod (x, y) {
devuelve esto. a + x + y;
}
}
// un método en un objeto literal
{
myMethod (x, y) {devuelve esto. a + x + y}
// alternativamente:
myMethod: function (x, y) {return this.a + x + y}
}
TIPO DE SISTEMA
Java tiene un sistema de tipos rudimentario, que admite tipos nominales, polimorfismo paramétrico y subtipo (con declaraciones de varianza). JavaScript no tiene un sistema de tipos.
Consulte la respuesta de Quildreen Motta a ¿Cuál es la diferencia entre los lenguajes de tipo dinámico y estático? para obtener detalles sobre qué son los sistemas de tipos y cómo se comparan entre sí los idiomas escritos / no escritos.
SEMÁNTICA DE EVALUACIÓN
Tanto Java como JavaScript usan semánticas de evaluación similares. Ambas son llamadas por valor (o llamadas por compartir, aunque nunca he visto personas que usen ese término fuera de CLU), ambas usan el orden aplicativo para evaluar argumentos, ambas tienen efectos colaterales sin restricciones .
Dicho esto, Java tiene un nivel superior que está restringido a declaraciones. Esto significa que dentro de un archivo fuente Java, todas las declaraciones y expresiones deben estar dentro de un método, y las cosas solo pueden suceder instanciando clases o invocando métodos. Esto es genial para cosas como la recarga en caliente.
En JavaScript, las declaraciones están permitidas en el nivel superior, por lo que puede colocar declaraciones y expresiones principalmente en cualquier lugar. Se evaluarán cuando se evalúe el archivo en sí. Esto es excelente para evitar los problemas con la sintaxis declarativa.
IMPLEMENTACIONES
La mayoría de las implementaciones de Java son un compilador anticipado (javac) que genera un código de bytes JVM, que luego es ejecutado por un JVM (esto puede ser un intérprete, un compilador de código nativo, hardware especializado que ejecuta el código de bytes directamente o cualquier combinación de estos: el Hotspot es una mezcla de intérprete y compilador, pero Graal, SubstrateVM y jaotc lo hacen más complicado). También hay implementaciones que compilan Java para otras cosas además del código de bytes JVM. GCC tenía soporte para Java, ExcelsiorJET todavía está vivo y utiliza optimizaciones guiadas por perfil, algunos compiladores Java-> JavaScript (GWT, JSweet, etc.). Puede haber intérpretes directos de Java (es decir, interpretar Java en lugar de JVM bytecode), pero no conozco ninguno.
Las implementaciones de JavaScript son probablemente más diversas. Históricamente, la mayoría de ellos eran intérpretes puros. En estos días es difícil encontrar una implementación de JavaScript que solo se interprete, la mayoría son una combinación de intérprete y compilación JIT (v8s más recientes, SpiderMonkey, Chakra, JSC), o solo compiladores JIT (v8s más antiguos). Puede haber compiladores AOT para el código nativo además del compilador de línea de base de v8, pero no conozco ninguno que sea notable (es decir, optimización).
Las máquinas virtuales para ambas implementaciones son sorprendentemente similares. v8 y Hotspot incluso confían en una arquitectura cercana, lo cual no es tan sorprendente dado que ambos obtuvieron su arquitectura de Self y fueron escritos por personas que trabajaron en Self VM. Pero además de eso, en su mayoría son JIT de método, ¿Graal es probablemente el único JIT de seguimiento?
Una diferencia importante en las implementaciones es probablemente la cantidad de compiladores JavaScript-> JavaScript, en comparación con los compiladores Java-> Java. El preempaque [2] es particularmente notable.
CONCEPTOS
Muchos conceptos se implementan en ambos idiomas:
- Ambos admiten programación orientada a objetos hasta cierto punto (los modelos difieren, más sobre esto más adelante).
- Ambos admiten estructuras procesales comunes (if, while, for).
- Ambos admiten el manejo de errores basado en excepciones con el desbobinado de la pila (más sobre esto más adelante), con algunas pequeñas diferencias.
- Ambos tienen valores primitivos además de los objetos, y los primitivos se encuadran automáticamente cuando necesitan actuar como objetos.
- Los operadores trabajan principalmente en primitivas, y no son sobrecargables (pero hay diferencias, más sobre esto más adelante).
- Ambos tienen un alcance léxico, con alcance de bloque (algunas peculiaridades de JavaScript aquí, más sobre esto más adelante).
- Ambos tienen un espacio de nombres global al que todo el código tiene acceso (nuevamente, algunas diferencias se describirán más adelante).
- Ambos tienen sintaxis declarativa para modificar un método o clase. Java tiene anotaciones, JavaScript tiene decoradores. La forma en que funcionan es muy diferente.
- Java tiene una torre numérica más expresiva, JavaScript solo tiene puntos flotantes de doble precisión. Sin embargo, se han propuesto enteros de precisión arbitraria para JavaScript.
- Si bien ambos tienen una estructura de conmutación, Java solo acepta constantes y JavaScript acepta cualquier valor (se comparan por igualdad estricta). Ambos interruptores incluyen fallos, como en C.
- Ambos tienen un tipo de matriz común. Pero Java es una pieza de memoria homogénea de tamaño fijo, asignada contiguamente. JavaScript es una estructura de datos heterogénea y dispersa, que está más cerca de una tabla hash.
- Ambos admiten la reflexión en tiempo de ejecución, aunque el soporte de Java es extremadamente limitado.
- Ambos admiten programación de orden superior, aunque el soporte de Java es extremadamente limitado (Java 8 aborda principalmente limitaciones sintácticas).
Funcionalidad específica de Java (por ahora):
- Modificadores de visibilidad (hay una propuesta para agregar miembros privados a las clases de JavaScript, pero no protegidos);
- Bloques sincronizados (JavaScript no tiene un subproceso preventivo definido en la especificación del lenguaje, y las implementaciones a menudo son de un solo subproceso; Nashorn y Rhino son algunas de las excepciones);
- Interfaces (principalmente declaraciones de tipo anteriores a Java 8, que no tiene sentido en JavaScript; en Java 8+ se duplican como mixins, que son compatibles de manera diferente en JavaScript);
- Java organiza programas en paquetes en general, y Java 8 admite una cosa llamada “módulos” que en realidad es una unidad de compilación, es decir, no es una característica para admitir la modularidad, sino para agrupar un conjunto de paquetes para el compilador. Lo que JavaScript llama módulos, sin embargo, es ambos: una unidad de compilación y una pieza de código reutilizable; que está más cerca de cómo se usa el “módulo” en cualquier otro lugar.
- Java tiene FFI para el código nativo descrito en la especificación del lenguaje. En JavaScript, cualquier cosa fuera de la especificación del lenguaje depende del host (implementación) para decidir.
- Java tiene tipos Enum, que son básicamente un conjunto de constantes. JavaScript no tiene miembros constantes, pero también dado que los objetos son más penetrantes en JavaScript, el equivalente de las enumeraciones terminan siendo solo objetos.
- Debido a que Java restringe las expresiones para que ocurran dentro de los cuerpos de los métodos, puede omitir ”
this
” cuando el campo / método no es ambiguo. Esto no es posible en JavaScript porque siempre es ambiguo.
- Java utiliza la inspección de pila para proporcionar políticas de seguridad para el código privilegiado. JavaScript no admite ningún tipo de separación de privilegios de este tipo (los reinos no son realmente eso).
Funcionalidad específica de JavaScript (por ahora):
- Puede organizar programas en módulos. Un módulo es un conjunto de instrucciones que da como resultado un conjunto de enlaces para exportar, y puede incluir un conjunto de enlaces para importar desde otros módulos, identificados por identificadores de módulo. JavaScript no lo restringe a módulos (los scripts pueden no ser módulos), y los módulos no se agregaron hasta 2015, aunque existían muchas soluciones de primera clase para usuarios.
- JavaScript admite generadores, que son funciones reanudables que capturan solo el marco de pila actual (piense en ello como una forma limitada de corutinas, si lo sabe).
- JavaScript admite concurrencia basada en eventos en la especificación. Define un bucle de eventos, promesas para capturar valores eventuales y funciones asincrónicas prestadas de C # y amigos. Como resultado, la concurrencia en JavaScript tiende a ser mucho más cooperativa y basada en el paso de mensajes que preventiva con memoria compartida.
- JavaScript tiene (y tenía) una declaración ”
with
” y un concepto de Registro de entorno de objetos. En esencia, un Object ER le permite tener una región de alcance léxico de primera clase, excepto que el alcance es mutable y admite la herencia, y suceden todo tipo de cosas terribles. El modo estricto afortunadamente elimina esta “característica”.
- JavaScript tiene proxies, que son un poderoso mecanismo de metaprogramación que permite anular los comportamientos de cualquier objeto. Sin embargo, esto está limitado por la cantidad de magia que rodea el modelo de objetos de JavaScript.
- JavaScript tiene reinos. Los reinos son entornos de ejecución separados, que son una parte importante para asegurar las interacciones entre dominios no confiables en páginas web. No serían necesarios si JavaScript no tuviera un espacio de nombres global.
- JavaScript especifica un requisito para las llamadas de cola adecuadas. Es decir, todas las llamadas en posición de cola deben realizarse en un espacio de pila limitado (la pila no puede crecer linealmente en el número de llamadas de función). Guy Steele ha hablado acerca de cómo las llamadas de cola son un requisito para la abstracción adecuada en los lenguajes orientados a objetos [3], a pesar de que con mayor frecuencia se asocian con lenguajes funcionales. A pesar de esto, hay muchos problemas que los implementadores plantearon sobre la implementación de esto en máquinas virtuales existentes [4], y en la práctica solo Apple lo ha implementado en JavaScript Core.
Bien, entonces hay muchas características específicas pequeñas en cada lado. Pero hablemos de las diferencias en cosas que se ven iguales en ambos idiomas al principio.
MODELO DE OBJETO
Java es un lenguaje simple basado en clases. Las clases son declaraciones de cosas comunes a un conjunto de objetos (las clases no son objetos en Java, lo que al menos evita parte de la complejidad con “¿cuál es la clase de la clase X?”), Cada clase puede heredar de otra clase, y las clases Definir un conjunto de miembros y métodos. Los miembros / métodos estáticos son compatibles, pero podría decirse que no están orientados a objetos. Los objetos son piezas simples que describen cuál es su clase (el diseño del objeto) y cuál es su estado de instancia, muy simple y muy compacto.
JavaScript es un lenguaje simple basado en prototipos. Solo hay objetos en el lenguaje, los objetos pueden heredar directamente de otro objeto, y los objetos son solo un conjunto de ranuras (donde una ranura es un par clave / valor, siendo el valor cualquier objeto o primitivo).
Además de esto, JavaScript admite muchas otras características:
- Getters y Setters permiten anular el comportamiento del acceso a la propiedad (”
ab
” da como resultado una llamada al método) y la asignación de propiedades (” ab = c
” da como resultado una llamada al método).
punto constante = {
_x: 0,
obtener un () {return this.x + 1}
establecer un (v) {this.x = v * 2}
};
punto.a; // => 1
punto.a = 2;
punto.a; // => 5
- Ningún objeto tiene un diseño fijo. Las ranuras se agregan al establecerles valores y se eliminan mediante el operador ”
delete
“. Esto hace que la optimización sea mucho más difícil, pero permite algunas expresiones idiomáticas interesantes. En esencia, en JavaScript, los diseños de objetos se definen iterativamente:
punto constante = {};
punto.x = 1;
punto.y = 2;
point.toString = function () {
devuelve this.x + ‘,’ + this.y;
}
point.toString (); // => ‘1, 2’
- La herencia está sujeta a retraso y puede cambiar en cualquier momento. Básicamente, puede cambiar de qué objeto hereda un objeto.
const a = {x: 1};
const b = {x: 2};
const c = {y: 3};
Object.setPrototypeOf (c, a); // c ahora hereda de un
cx; // => 1
cy; // => 3
Object.setPrototypeOf (c, b); // c ahora hereda de b
cx; // => 2
cy; // => 3
- ECMAScript 2015 presenta Símbolos, que son una especie de clave única (en contraste con el espacio de nombres plano de las claves con valores de cadena).
const foo = {};
const a1 = Símbolo (“a”);
const a2 = Símbolo (“a”);
foo [a1] = 1;
foo [a2] = 2;
foo [a1]; // => 1
foo [a2]; // => 2
- Las funciones regulares hacen el trabajo de funciones, métodos, constructores y clases. La declaración de ”
class
” de ECMAScript 2015 tiene en gran medida la misma semántica que las funciones regulares para la compatibilidad con versiones anteriores.
- No se garantiza que ”
new Foo()
” devuelva un objeto Foo
. Si la función Foo
devuelve cualquier valor que sea un objeto, ese valor se devuelve desde la expresión.
- “
ab
” cuando “ b
” es un método de “ a
” no vincula el método a “ a
”, como cabría esperar. Java no tenía este problema porque no era posible referirse a un método directamente hasta Java 8, y ” a::b
” de Java 8 vincula el método a ” a
“. Esto termina causando mucha confusión en JavaScript sobre lo que significa ” this
” una vez que las personas comienzan a pasar los métodos, probablemente porque las personas no piensan en ” this
” como un argumento habitual proporcionado en el momento en que se llaman los métodos.
MANEJO DE EXCEPCIONES
Tanto JavaScript como Java tienen objetos de excepción, que pueden ser arrojados por cualquier código con una declaración ” throw
” y capturados con una declaración ” try
“. Cuando se detecta, la pila se desenrolla (es decir, no se puede transferir el control al lugar donde ocurrió la instrucción ” throw
“), y se ejecuta el bloque ” catch
” de la instrucción ” try
“, con una variable local vinculada al valor arrojado.
Algunas diferencias entre ellos:
- En Java, la mayoría de las excepciones están marcadas y deben capturarse o aparecer en la firma de la función.
- En Java es posible tener más de un bloque catch, ya que el bloque catch describe qué tipo de excepción espera. Se utilizará la primera coincidencia (donde el valor arrojado es un subtipo de la excepción descrita) en un bloque catch.
- En JavaScript puede arrojar cualquier valor, incluso primitivas. En Java solo puedes lanzar algo que sea Throwable.
OPERADORES
En su mayoría tienen el mismo conjunto de operadores comunes. Pero algunos de ellos realmente no hacen lo que esperarías en JavaScript.
- Todos los operadores en JavaScript que trabajan en primitivas harán conversiones de tipo, posiblemente llamando a métodos arbitrarios en los operandos. En Java, hay algún tipo de conversión con primitivas concatenadas en una cadena, pero eso es todo.
- En JavaScript, ”
a instanceof b
” es una llamada al método (” b[Symbol.hasInstance](a)
“), sin embargo, aún requiere que ” b
” sea una función (o clase) de constructor en este caso.
ALCANCE LEXICAL
Java y JavaScript usan el alcance léxico, y ambos admiten el alcance de bloque. JavaScript también admite el alcance de funciones (con ” var
“). Esto puede ser confuso para las personas que esperan de lenguajes basados en bloques como C y Jav