¿Cuál es la diferencia entre cadenas primitivas y objetos String en JavaScript?

Resuelto The Alpha asked hace 11 años • 12 respuestas

Tomado de MDN

Los literales de cadena (indicados por comillas dobles o simples) y las cadenas devueltas por llamadas a cadenas en un contexto sin constructor (es decir, sin usar la palabra clave new) son cadenas primitivas. JavaScript convierte automáticamente primitivos en objetos String, de modo que es posible utilizar métodos de objetos String para cadenas primitivas. En contextos donde se debe invocar un método en una cadena primitiva o se produce una búsqueda de propiedad, JavaScript envolverá 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 de aplicarse methoda la cadena.

Pero en este caso de prueba , el resultado es el contrario. 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 detallan 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 según 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 ?

The Alpha avatar Jun 23 '13 06:06 The Alpha
Aceptado

JavaScript tiene dos categorías de tipos principales, primitivos y objetos.

var s = 'test';
var ss = new String('test');

Los patrones de comillas simples y dobles son idénticos en términos de funcionalidad. Aparte de eso, el comportamiento que intentas nombrar se llama auto-boxing. Entonces, lo que realmente sucede es que una primitiva se convierte a su tipo contenedor cuando se invoca un método del tipo contenedor. Dicho simple:

var s = 'test';

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, ¿qué pasa cuando lo haces, s.charAt(i)por ejemplo?

Como sno es una instancia de String, JavaScript encuadrará automáticamente s, que tiene typeof stringcomo tipo contenedor, String, con typeof objecto más precisamente s.valueOf(s).prototype.toString.call = [object String].

El comportamiento de boxeo automático avanza sy retrocede 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 autoboxeo y Object.prototype.valueOftiene diferentes efectos.

Si desea forzar el boxeo automático o convertir una primitiva a su tipo contenedor, puede usar Object.prototype.valueOf, pero el comportamiento es diferente. Basado en una amplia variedad de escenarios de prueba, el autoboxing solo aplica los métodos "requeridos", sin alterar la naturaleza primitiva de la variable. Por eso obtienes una mejor velocidad.

flavian avatar Jun 22 '2013 23:06 flavian

Esto depende bastante de la implementación, pero lo intentaré. Daré un ejemplo con el V8, pero supongo que otros motores utilizan enfoques similares.

Una primitiva de cadena se analiza en un v8::Stringobjeto. Por lo tanto, se pueden invocar métodos directamente en él como lo menciona jfriend00 .

Un objeto String, por otro lado, se analiza en a v8::StringObjectque se extiende Objecty, además de ser un objeto completo, sirve como contenedor para v8::String.

Ahora es lógico, una llamada a new String('').method()tiene que desempaquetar this v8::StringObjectantes v8::Stringde ejecutar el método, por lo que es más lento.


En muchos otros idiomas, 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 boxeo automático de las primitivas (como también se menciona en la respuesta de flav ), es decir, cómo los valores primitivos-y de JavaScript 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 manera informativa en la especificación ES5 anotada. con respecto a la resolución de propiedades (y "métodos"¹) de valores primitivos:

NOTA No se puede acceder al objeto que se puede crear en el paso 1 fuera del método anterior. Una implementación podría optar por evitar la creación real del objeto. [...]

En un nivel muy bajo, las cadenas se implementan con mayor frecuencia como valores escalares inmutables. Ejemplo de estructura contenedora:

StringObject > String (> ...) > char[]

Cuanto más lejos estés del primitivo, más tardarás en llegar a él. En la práctica, Stringlas primitivas son mucho más frecuentes que StringObjects, por lo que no es una sorpresa que los motores agreguen métodos a la Clase de objetos correspondientes (interpretados) de las primitivas String en lugar de realizar conversiones entre Stringy StringObjectcomo 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.

Fabrício Matté avatar Jun 22 '2013 23:06 Fabrício Matté

En el caso de una cadena literal no podemos asignar propiedades.

var x = "hello" ;
x.y = "world";
console.log(x.y); // this will print undefined

Mientras que en el caso de String Object podemos asignar propiedades

var x = new String("hello");
x.y = "world";
console.log(x.y); // this will print world
refactor avatar Apr 14 '2016 06:04 refactor

Si usa new, está indicando explícitamente que desea crear una instancia de un Objeto . Por lo tanto, new Stringse produce un Objeto que envuelve la primitiva Cadena , lo que significa que cualquier acción sobre él implica una capa adicional de trabajo.

typeof new String(); // "object"
typeof '';           // "string"

Como son de diferentes tipos, su intérprete de JavaScript también puede optimizarlos de manera diferente, como se menciona en los comentarios .

Paul S. avatar Jun 22 '2013 23:06 Paul S.