variable template quitar concatenar con como javascript null undefined string-concatenation

quitar - template string javascript



Comportamiento de concatenación de cadenas JavaScript con valores nulos o indefinidos (5)

Como sabrá, en JavaScript '''' + null = "null" y '''' + undefined = "undefined" (en la mayoría de los navegadores puedo probar: Firefox, Chrome e IE). Me gustaría saber el origen de esta rareza (¿qué diablos le pasó a Brendan Eich ?!) y si hay algún objetivo para cambiarla en una futura versión de ECMA. De hecho, es bastante frustrante tener que hacer ''sthg'' + (var || '''') para concatenar cadenas con variables y usar un framework de terceros como Underscore u otro para eso está usando un martillo para golpear las uñas con gelatina.

Editar:

Para cumplir con los criterios requeridos por StackOverflow y aclarar mi pregunta, es triple:

  • ¿Cuál es la historia detrás de la rareza que hace que JS convierta null o undefined a su valor de cadena en String concatenación de String ?
  • ¿Hay alguna posibilidad de un cambio en este comportamiento en futuras versiones de ECMAScript?
  • ¿Cuál es la manera más bonita de concatenar String con undefined objeto null o undefined potencial sin caer en este problema (obtener un "undefined" de "null" en el medio de la Cadena)? Por los criterios subjetivos más bonitos , quiero decir: cortos, limpios y efectivos. No hace falta decir que '''' + (obj ? obj : '''') no es realmente bonito ...

¿Cuál es la forma más bonita de concatenar Cadena con un objeto nulo o indefinido potencial sin caer en este problema [...]?

Hay varias formas, y usted las mencionó parcialmente. Para abreviar, la única manera limpia que puedo pensar es una función:

var Strings = {}; Strings.orEmpty = function( entity ) { return entity || ""; }; // usage var message = "This is a " + Strings.orEmpty( test );

Por supuesto, puede (y debe) cambiar la implementación real para satisfacer sus necesidades. Y esta es la razón por la que creo que este método es superior: introdujo la encapsulación.

Realmente, solo tienes que preguntar cuál es la forma "más bonita", si no tienes encapsulado. Te haces esta pregunta porque ya sabes que te vas a meter en un lugar donde ya no puedes cambiar la implementación, así que quieres que sea perfecto de inmediato. Pero esa es la cuestión: los requisitos, las vistas e incluso los entornos cambian. Ellos evolucionan. Entonces, ¿por qué no te permites cambiar la implementación con tan poco como adaptar una línea y tal vez una o dos pruebas?

Podrías llamar a esto trampa, porque realmente no responde cómo implementar la lógica real. Pero ese es mi punto: no importa. Bueno, tal vez un poco. Pero realmente, no hay necesidad de preocuparse por lo simple que sería cambiar. Y como no está en línea, también se ve mucho más bonito, ya sea que lo implemente de esta manera o de una manera más sofisticada.

Si, a lo largo de tu código, sigues repitiendo el || en línea, te encuentras con dos problemas:

  • Usted duplica el código.
  • Y debido a que duplica el código, hace que sea difícil de mantener y cambiar en el futuro.

Y estos son dos puntos comúnmente conocidos como antipatrones cuando se trata de desarrollo de software de alta calidad.

Algunas personas dirán que esto es demasiado sobrecargado; ellos hablarán sobre el desempeño. No tiene sentido. Por un lado, esto apenas agrega gastos generales. Si esto es lo que le preocupa, eligió el idioma equivocado. Incluso jQuery usa funciones. La gente necesita superar la micro-optimización.

La otra cosa es: puedes usar un código "compilador" = minificador. Las buenas herramientas en esta área intentarán detectar qué enunciados se alinearán durante el paso de compilación. De esta manera, mantendrá su código limpio y mantenible, y aún puede obtener la última caída de rendimiento si todavía cree en él o si realmente tiene un entorno donde esto importa.

Por último, ten fe en los navegadores. Van a optimizar el código y hacen un muy buen trabajo en estos días.


¿Hay alguna posibilidad de un cambio en este comportamiento en futuras versiones de ECMAScript?

Yo diría que las posibilidades son muy escasas. Y hay varias razones:

Ya sabemos cómo son ES5 y ES6

Las futuras versiones de ES ya están hechas o en borrador. Ninguno de los dos, afaik, cambia este comportamiento. Y lo que hay que tener en cuenta aquí es que llevará años establecer estos estándares en los navegadores en el sentido de que puede escribir aplicaciones con estos estándares sin depender de herramientas de proxy que compilen el Javascript real.

Solo trata de estimar la duración. Ni siquiera ES5 cuenta con el respaldo total de la mayoría de los navegadores que existen y probablemente demore unos años más. ES6 aún no está completamente especificado. De la nada, estamos viendo al menos otros cinco años.

Los navegadores hacen sus propias cosas

Los navegadores son conocidos por tomar sus propias decisiones sobre ciertos temas. No sabe si todos los navegadores soportan completamente esta característica exactamente de la misma manera. Por supuesto, usted sabrá una vez que es parte del estándar, pero a partir de ahora, incluso si se anunció que formará parte de ES7, solo sería una especulación en el mejor de los casos.

Y los navegadores pueden tomar su propia decisión aquí especialmente porque:

Este cambio se está rompiendo

Una de las cosas más importantes de los estándares es que generalmente intentan ser compatibles con versiones anteriores. Esto es especialmente cierto para la web donde el mismo código debe ejecutarse en todo tipo de entornos.

Si el estándar introduce una nueva función y no es compatible con navegadores antiguos, eso es una cosa. Dígale a su cliente que actualice su navegador para usar el sitio. Pero si actualiza su navegador y de repente la mitad de los cortes de internet para usted, eso es un error uhm-no.

Claro, es poco probable que este cambio en particular rompa muchos guiones. Pero eso suele ser un argumento pobre porque un estándar es universal y debe tener en cuenta todas las posibilidades. Solo considera

"use strict";

como la instrucción de cambiar al modo estricto. Muestra todo el esfuerzo que un estándar pone en tratar de hacer que todo sea compatible, porque podrían haber hecho del modo estricto el modo predeterminado (e incluso el único). Pero con esta instrucción inteligente, permite que el código viejo se ejecute sin cambios y aún así puede aprovechar el nuevo y más estricto modo.

Otro ejemplo de compatibilidad con versiones anteriores: el operador === . == es fundamentalmente defectuoso (aunque algunas personas no están de acuerdo) y podría haber cambiado su significado. En su lugar, se introdujo === , lo que permite que el código viejo se ejecute sin interrupción; al mismo tiempo, permite que se escriban nuevos programas usando un control más estricto.

Y para que un estándar rompa la compatibilidad, tiene que haber una muy buena razón. Lo que nos lleva a

Simplemente no hay una buena razón

Sí, te molesta. Eso es comprensible. Pero, en última instancia, no es nada que no se pueda resolver muy fácilmente . Usar || , escribe una función, lo que sea. Puedes hacerlo funcionar casi sin costo. Entonces, ¿cuál es realmente el beneficio de invertir todo el tiempo y esfuerzo en analizar este cambio que sabemos que se está rompiendo de todos modos? Simplemente no veo el punto.

Javascript tiene varios puntos débiles en su diseño. Y se ha convertido cada vez más en un problema mayor a medida que el idioma se volvió cada vez más importante y poderoso. Pero si bien hay muy buenas razones para cambiar mucho su diseño, otras cosas simplemente no están destinadas a ser cambiadas .

Descargo de responsabilidad: esta respuesta se basa en parte en las opiniones.


La especificación de ECMA

Para dar cuerpo a la razón por la que se comporta de esta manera en términos de especificaciones, este comportamiento ha estado presente desde la primera versión . La definición allí y en 5.1 son semánticamente equivalentes, mostraré las definiciones 5.1.

Sección 11.6.1: El operador de adición (+)

El operador de adición realiza una concatenación de cadenas o una suma numérica.

La producción AdditiveExpression : AdditiveExpression + MultiplicativeExpression se evalúa de la siguiente manera:

  1. Deje que lref sea ​​el resultado de evaluar AdditiveExpression.
  2. Deje lval ser GetValue ( lref ).
  3. Deje que rref sea ​​el resultado de la evaluación de Expresión multiplicativa.
  4. Deje que rval sea ​​GetValue ( rref ).
  5. Deje que lprim sea ​​ToPrimitive ( lval ).
  6. Deje que rprim sea ​​ToPrimitive ( rval ).
  7. Si Tipo ( lprim ) es Cadena o Tipo ( rprim ) es Cadena, entonces
    a. Devuelve la cadena que es el resultado de concatenar ToString ( lprim ) seguido de ToString ( rprim )
  8. Devuelve el resultado de aplicar la operación de adición a ToNumber ( lprim ) y ToNumber ( rprim ). Ver la Nota debajo 11.6.3.

Entonces, si cualquier valor termina siendo una String , entonces ToString se usa en ambos argumentos (línea 7) y aquellos se concatenan (línea 7a). ToPrimitive devuelve todos los valores no objeto sin cambios, por lo que undefined se modifican los valores null e undefined :

Sección 9.1 ToPrimitive

La operación abstracta ToPrimitive toma un argumento de entrada y un argumento opcional PreferredType . La operación abstracta ToPrimitive convierte su argumento de entrada a un tipo que no es un objeto ... La conversión se produce de acuerdo con la Tabla 10:

Para todos los tipos que no sean Object , incluidos tanto Null como Undefined , [t]he result equals the input argument (no conversion). Así que ToPrimitive no hace nada aquí.

Finalmente, la Sección 9.8 ToString

La operación abstracta ToString convierte su argumento en un valor de tipo String de acuerdo con la Tabla 13:

La Tabla 13 muestra "undefined" para el tipo Undefined y "null" para el tipo Null .

¿Cambiará? ¿Es incluso una "rareza"?

Como han señalado otros, es poco probable que esto cambie ya que rompería la compatibilidad con versiones anteriores (y no aportaría ningún beneficio real), más aún dado que este comportamiento es el mismo desde la versión de 1997 de la especificación. Yo tampoco lo consideraría una rareza.

Si tuviera que cambiar este comportamiento, ¿cambiaría la definición de ToString por null e undefined o sería un caso especial el operador de suma para estos valores? ToString se usa en muchos, muchos lugares a lo largo de la especificación y "null" parece ser una elección indiscutible para representar null . Solo para dar un par de ejemplos, en Java "" + null es la cadena "null" y en Python str(None) es la cadena "None" .

Solución

Otros han dado buenas soluciones, pero agregaría que dudo que quiera usar entity || "" entity || "" como estrategia, ya que se convierte en true en "true" pero false en "" . La combinación de matriz en esta respuesta tiene el comportamiento más esperado, o puede cambiar la implementación de esta respuesta para verificar entity == null (tanto null == null como undefined == null son verdaderos).


Para agregar null y '''' deben cumplir un criterium de tipo común mínimo que en este caso es un tipo de cadena.

null se convierte en "nulo" por este motivo y como son cadenas, los dos se concatenan.

Lo mismo sucede con los números:

4 + '''' = ''4''

ya que hay una cadena allí que no se puede convertir a ningún número, por lo que el 4 se convertirá a cadena en su lugar.


Puede usar Array.prototype.join para ignorar undefined y null :

[''a'', ''b'', void 0, null, 6].join(''''); // ''ab6''

De acuerdo con la spec :

Si el elemento undefined está undefined o es null , Let the next be the Empty String; de lo contrario, deja que sea ToString ( elemento ).

Dado que,

  • ¿Cuál es la historia detrás de la rareza que hace que JS convierta null o undefined a su valor de cadena en String concatenación de String ?

    De hecho, en algunos casos, el comportamiento actual tiene sentido.

    function showSum(a,b) { alert(a + '' + '' + b + '' = '' + (+a + +b)); }

    Por ejemplo, si se llama a la función anterior sin argumentos, undefined + undefined = NaN probablemente sea mejor que + = NaN .

    En general, creo que si quiere insertar algunas variables en una cadena, tiene sentido mostrar undefined o null . Probablemente, Eich pensó eso también.

    Por supuesto, hay casos en los que ignorarlos sería mejor, como cuando se unen cadenas. Pero para esos casos puedes usar Array.prototype.join .

  • ¿Hay alguna posibilidad de un cambio en este comportamiento en futuras versiones de ECMAScript?

    Probablemente no.

    Como ya existe Array.prototype.join , modificar el comportamiento de la concatenación de cadenas solo generaría inconvenientes, pero no ventajas. Además, rompería los códigos antiguos, por lo que no sería retrocompatible.

  • ¿Cuál es la forma más bonita de concatenar Cadena con potencial null o undefined ?

    Array.prototype.join parece ser el más simple. Si es el más lindo o no, puede basarse en las opiniones.