Tomado de MDN
Los literales de cadena (indicados por comillas dobles o simples) y las cadenas devueltas de llamadas de cadena en un contexto que no es de constructor (es decir, sin usar la nueva palabra clave) son cadenas primitivas. JavaScript convierte automáticamente primitivas en objetos String, por lo que es posible utilizar métodos de objetos String para cadenas primitivas. En contextos donde se va a invocar un método en una cadena primitiva o se produce una búsqueda de propiedad, JavaScript ajustará automáticamente la cadena primitiva y llamará al método o realizará la búsqueda de propiedad.
Entonces, pensé (lógicamente) que las operaciones (llamadas a métodos) en primitivas de cadena deberían ser más lentas que las operaciones en Objetos de cadena porque cualquier primitiva de cadena se convierte en Objeto de cadena (trabajo adicional) antes del method
se aplique en la cadena.
Pero en este caso de prueba , el resultado es opuesto. El bloque de código 1 se ejecuta más rápido que el bloque de código 2 , ambos bloques de código se dan a continuación:
bloque de código 1:
var s = '0123456789';
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
bloque de código 2:
var s = new String('0123456789');
for (var i = 0; i < s.length; i++) {
s.charAt(i);
}
Los resultados varían en los navegadores, pero el bloque de código 1 siempre es más rápido. ¿Alguien puede explicar esto, por qué el bloque de código 1 es más rápido que el bloque de código 2 ?
fuente
new String
introduce otra capa transparente de envoltura de objetos .typeof new String(); //"object"
'0123456789'.charAt(i)
?code block-1
es más rápido.Respuestas:
JavaScript tiene dos categorías principales de tipos, primivitas y objetos.
Los patrones de comillas simples / dobles son idénticos en términos de funcionalidad. Aparte de eso, el comportamiento que está tratando de nombrar se llama auto-boxing. Entonces, lo que realmente sucede es que una primitiva se convierte a su tipo de contenedor cuando se invoca un método del tipo de contenedor. En pocas palabras:
Es un tipo de datos primitivo. No tiene métodos, no es más que un puntero a una referencia de memoria de datos sin procesar, lo que explica la velocidad de acceso aleatorio mucho más rápida.
Entonces que pasa cuando lo haces
s.charAt(i)
por ejemplo?Dado
s
que no es una instancia deString
, JavaScript se auto-boxs
, que tienetypeof string
su tipo de contenedor,,String
contypeof object
o más precisamentes.valueOf(s).prototype.toString.call = [object String]
.El comportamiento de auto-boxing se proyecta
s
hacia adelante y hacia atrás a su tipo de contenedor según sea necesario, pero las operaciones estándar son increíblemente rápidas ya que se trata de un tipo de datos más simple. Sin embargo, el auto-boxing yObject.prototype.valueOf
tiene diferentes efectos.Si desea forzar el auto-boxing o lanzar una primitiva a su tipo de envoltura, puede usar
Object.prototype.valueOf
, pero el comportamiento es diferente. Basado en una amplia variedad de escenarios de prueba, el auto-boxing solo aplica los métodos 'requeridos', sin alterar la naturaleza primitiva de la variable. Por eso obtienes una mejor velocidad.fuente
Esto depende bastante de la implementación, pero intentaré. Lo ejemplificaré con V8, pero supongo que otros motores usan enfoques similares.
Una cadena primitiva se analiza en un
v8::String
objeto. Por lo tanto, los métodos se pueden invocar directamente en él como lo menciona jfriend00 .Un objeto String, por otro lado, se analiza a un objeto
v8::StringObject
que se extiendeObject
y, además de ser un objeto completo, sirve como envoltorio parav8::String
.Ahora es lógico, una llamada a
new String('').method()
tiene que desempacar estev8::StringObject
'sv8::String
antes de ejecutar el método, por lo tanto es más lento.En muchos otros lenguajes, los valores primitivos no tienen métodos.
La forma en que MDN lo expresa parece ser la forma más sencilla de explicar cómo funciona el auto-boxing de los primitivos (como también se menciona en la respuesta de flav ), es decir, cómo JavaScript's primitive-y valores de pueden invocar métodos.
Sin embargo, un motor inteligente no convertirá una cadena primitiva-y en un objeto String cada vez que necesite llamar a un método. Esto también se menciona de forma informativa en la especificación ES5 anotada. con respecto a la resolución de propiedades (y "métodos" ¹) de valores primitivos:
A un nivel muy bajo, las cadenas se implementan con mayor frecuencia como valores escalares inmutables. Ejemplo de estructura de envoltura:
Cuanto más lejos estés del primitivo, más tiempo tardarás en llegar a él. En la práctica, las
String
primitivas son mucho más frecuentes queStringObject
s, por lo que no es una sorpresa que los motores agreguen métodos a la Clase de los objetos (interpretados) correspondientes de las primitivas de cadena en lugar de realizar conversiones de ida y vuelta entreString
yStringObject
como sugiere la explicación de MDN.¹ En JavaScript, "método" es solo una convención de nomenclatura para una propiedad que se resuelve en un valor de tipo función.
fuente
=]
Ahora me pregunto si la explicación de MDN está ahí solo porque parece ser la forma más fácil de entender el auto-boxing o si hay alguna referencia a él en la especificación ES. Al leer toda la especificación en el momento de verificar, recordaré actualice la respuesta si alguna vez encuentro una referencia.En el caso de una cadena literal, no podemos asignar propiedades.
Mientras que en el caso de String Object podemos asignar propiedades
fuente
String
objetos. ¡Gracias!Literal de cadena:
Los literales de cadena son inmutables, lo que significa que una vez que se crean, su estado no se puede cambiar, lo que también los hace seguros para subprocesos.
a==b
el resultado será 'verdadero' ambas cadenas refieren el mismo objeto.Objeto de cadena:
Aquí, se crean dos objetos diferentes y tienen diferentes referencias:
a==b
El resultado será falso, porque tienen referencias diferentes.fuente
a
yb
intenta asignara[0] = 'X'
que se puede ejecutar con éxito pero no funcionará como se podría esperarSi lo usa
new
, está indicando explícitamente que desea crear una instancia de un objeto . Por lo tanto,new String
está produciendo un Objeto que envuelve la primitiva Cadena , lo que significa que cualquier acción sobre él implica una capa adicional de trabajo.Como son de diferentes tipos, su intérprete de JavaScript también puede optimizarlos de manera diferente, como se menciona en los comentarios .
fuente
Cuando declaras:
creas una cadena primitiva. Esa primitiva de cadena tiene métodos que le permiten llamar a métodos sin convertir la primitiva en un objeto de primera clase. Entonces, su suposición de que esto sería más lento porque la cadena debe convertirse en un objeto no es correcta. No es necesario convertirlo en un objeto. La propia primitiva puede invocar los métodos.
Convertirlo en un objeto completo (que le permite agregarle nuevas propiedades) es un paso adicional y no acelera las operaciones de cadena (de hecho, su prueba muestra que las hace más lentas).
fuente
String.prototype
?var s = '0123456789';
es un valor primitivo, ¿cómo puede este valor tener métodos? ¡Estoy confundido!Puedo ver que esta pregunta se ha resuelto hace mucho tiempo, hay otra distinción sutil entre los literales de cadena y los objetos de cadena, ya que nadie parece haberlo tocado, pensé en escribirlo para completarlo.
Básicamente, otra distinción entre los dos es cuando se usa eval. eval ('1 + 1') da 2, mientras que eval (new String ('1 + 1')) da '1 + 1', por lo que si cierto bloque de código se puede ejecutar tanto 'normalmente' o con eval, podría conducir a resultados extraños
fuente
new String("")
devuelve un objeto, y eval solo evalúa la cadena, y devuelve todo lo demás como estáLa existencia de un objeto tiene poco que ver con el comportamiento real de una cadena en los motores ECMAScript / JavaScript, ya que el alcance raíz simplemente contendrá objetos de función para esto. Por lo tanto, se buscará y ejecutará la función charAt (int) en el caso de una cadena literal.
Con un objeto real, agrega una capa más donde el método charAt (int) también se busca en el objeto mismo antes de que se active el comportamiento estándar (igual que el anterior). Aparentemente, se ha realizado una cantidad sorprendentemente grande de trabajo en este caso.
Por cierto, no creo que las primitivas se conviertan en objetos, pero el motor de secuencia de comandos simplemente marcará esta variable como tipo de cadena y, por lo tanto, puede encontrar todas las funciones proporcionadas para que parezca que invocas un objeto. No olvide que este es un tiempo de ejecución de script que funciona con principios diferentes a los de un tiempo de ejecución OO.
fuente
La mayor diferencia entre una primitiva de cadena y un objeto de cadena es que los objetos deben seguir esta regla para el
==
operador :Entonces, mientras que las primitivas de cadena tienen una conveniencia
==
que compara el valor, no tiene suerte cuando se trata de hacer que cualquier otro tipo de objeto inmutable (incluido un objeto de cadena) se comporte como un tipo de valor.(Otros han notado que un objeto de cadena es técnicamente mutable porque puede agregarle propiedades. Pero no está claro para qué es útil; el valor de cadena en sí no es mutable).
fuente
El código se optimiza antes de ejecutarlo mediante el motor javascript. En general, los micro benchmarks pueden ser engañosos porque los compiladores e intérpretes reorganizan, modifican, eliminan y realizan otros trucos en partes de su código para que se ejecute más rápido. En otras palabras, el código escrito indica cuál es el objetivo, pero el compilador y / o el tiempo de ejecución decidirán cómo lograrlo.
El bloque 1 es más rápido principalmente debido a: var s = '0123456789'; siempre es más rápido que var s = new String ('0123456789'); debido a la sobrecarga de la creación de objetos.
La parte del bucle no es la que causa la desaceleración porque el intérprete puede insertar chartAt (). Intente quitar el bucle y vuelva a ejecutar la prueba, verá que la relación de velocidad será la misma que si no se hubiera eliminado el bucle. En otras palabras, para estas pruebas, los bloques de bucle en el momento de la ejecución tienen exactamente el mismo código de bytes / código de máquina.
Para estos tipos de micro evaluaciones comparativas, mirar el código de bytes o el código de máquina proporcionará una imagen más clara.
fuente
En Javascript, los tipos de datos primitivos como la cadena son un bloque de construcción no compuesto. Esto significa que son solo valores, nada más:
let a = "string value";
forma predeterminada no hay métodos integrados como toUpperCase, toLowerCase, etc.Pero, si intentas escribir:
Esto no arrojará ningún error, sino que funcionarán como deberían.
Que pasó ? Bueno, cuando intenta acceder a una propiedad de una cadena,
a
Javascript coacciona la cadena a un objetonew String(a);
conocido como objeto contenedor .Este proceso está vinculado al concepto denominado constructores de funciones en Javascript, donde las funciones se utilizan para crear nuevos objetos.
Cuando escribe
new String('String value');
aquí String es un constructor de funciones, que toma un argumento y crea un objeto vacío dentro del alcance de la función, este objeto vacío se asigna a este y, en este caso, String proporciona todas las funciones integradas conocidas que mencionamos antes. y tan pronto como se completa la operación, por ejemplo, si se realiza una operación en mayúsculas, el objeto contenedor se descarta.Para probar eso, hagamos esto:
Aquí la salida no estará definida. Por qué ? En este caso, Javascript crea un objeto String contenedor, establece una nueva propiedad addNewProperty y descarta el objeto contenedor inmediatamente. es por eso que no se define. El pseudocódigo se vería así:
fuente
podemos definir String de 3 formas
// también podemos crear usando 4. var d = a + '';
Verifique el tipo de cadenas creadas usando el operador typeof
cuando comparas a y b var
a==b ( // yes)
cuando comparas el objeto String
fuente