2011-12-20 13 views
5

Esta pregunta ha salido de another, que se refiere al comportamiento de console.dir con literales de cadena. En particular, vea los comentarios en my answer.¿Por qué los métodos de String.prototype están disponibles para los literales de cadena?

Como todos sabemos, String objetos en JavaScript tiene una serie de métodos. Esos métodos están definidos en el objeto String.prototype. String.prototype.toUpperCase por ejemplo. Por tanto, podemos hacer cosas como esta:

var s = new String("hello"), 
    s2 = s.toUpperCase();  //toUpperCase is a method on String.prototype 

Sin embargo, también podemos hacer esto:

var s = "hello",    //s is a string literal, not an instance of String 
    s2 = s.toUpperCase(); 

Claramente, el intérprete de JavaScript está haciendo algún tipo de conversión A/fundido cuando se llama a un método de String.prototype en una cadena literal. Sin embargo, no puedo encontrar ninguna referencia a esto en el spec.

Tiene sentido, porque de lo contrario tendrías que copiar explícitamente cada cadena literal a un objeto String antes de que pudieras usar cualquiera de los métodos, y eso sería bastante molesto.

Así que mi pregunta es, ¿dónde se describe esta funcionalidad, y estoy en lo cierto al suponer que el valor literal se transfiere temporalmente a una instancia de String? ¿Estoy pensando demasiado y me estoy perdiendo algo obvio?

+1

Por cierto, la notación 'new String (value)' es 100% inútil ... no lo use. –

+0

Sé que es. La pregunta es por qué. ¿Dónde se describe esto en la especificación? –

+1

Un literal de cadena es un objeto de cadena: 'typeof" hello "===" string "' devuelve true. ¿Eso llega al problema? – bbg

Respuesta

7

Se define aquí:

La siguiente [[Get]] método interno es utilizado por GetValue cuando V es una referencia propiedad con un valor base primitiva. Se llama utilizando base como su valor y con la propiedad P como argumento. Se toman los siguientes pasos de :

  1. Let O be ToObject (base).
  2. Deje que el desc sea el resultado de llamar al método interno [[GetProperty]] de O con el nombre de propiedad P.
  3. Si desc no está definido, devuelva indefinido.
  4. Si IsDataDescriptor (desc) es verdadero, devuelva desc. [[Value]].
  5. De lo contrario, IsAccessorDescriptor (desc) debe ser verdadero, así que permita que getter sea desc. [[Obtener]].
  6. Si getter no está definido, devuelva indefinido.
  7. Devuelve el resultado invocando el método interno [[Call]] de getter que proporciona base como este valor y no proporciona ningún argumento.

NOTA El objeto que se puede crear en el paso 1 no es accesible fuera del método anterior. Una implementación puede elegir evitar la creación real del objeto. La única situación en la que tal acceso a la propiedad real que utiliza este método interno puede tener un efecto visible es cuando invoca una función de acceso.

Fuente:http://es5.github.com/#x8.7.1

El valor de cadena primitiva es coaccionado a un objeto en el paso 1.


Ejemplo 1

var str = 'some string'; 
str = str.toUpperCase(); 

Aquí, la expresión str.toUpperCase se evalúa de acuerdo con la semántica definidas en 11.2.1 Property Accessors:

  1. El identificador str se evalúa de acuerdo con la resolución de identificador (ver 10.2.2.1 GetIdentifierReference). El resultado es una referencia cuyo valor base es el registro de entorno del entorno léxico actual, y cuyo nombre de referencia es "str". Esta referencia es baseReference.
  2. baseValue se determina ejecutando GetValue(baseReference). Como baseReference no es una referencia de propiedad (su valor base no es un objeto o un valor primitivo, sino un registro de entorno), se llama al método GetBindingValue() para recuperar el valor de la referencia. Este método devuelve el valor de la variable local str, es decir, el valor de cadena primitivo 'some string'. Este valor es baseValue.
  3. propertyNameValue se evalúa como el valor de cadena primitivo 'toUpperCase'. (He acortado este proceso un poco en aras de la simplicidad.)
  4. Se crea una nueva referencia, cuya base es el valor baseValue y cuyo nombre hace referencia es propertyNameValue.

Por lo tanto, hay dos referencias que participan en este proceso:

  • str (valor base: el registro medio ambiente, nombre de referencia: 'str')
  • str.toUpperCase (valor base: 'some string', nombre de referencia: 'toUpperCase')

Finalmente, el operador de invocación () se ejecuta en la última referencia. El valor de esa referencia se determina de acuerdo con la semántica definida en la parte superior de esta respuesta.

Ejemplo 2

var str = 'some string'.toUpperCase(); 

Aquí, la expresión 'some string'.toUpperCase se evalúa de acuerdo con la misma semántica "propiedad de acceso" como en el ejemplo 1:

  1. La cadena literal 'some string' obviamente evalúa a la valor de cadena primitivo 'some string'. Esta es la baseReference. (No permita que el nombre lo confunda; este es un valor de cadena, no una referencia).
  2. El baseValue se determina al ejecutar GetValue(baseReference). Como baseReference no es una referencia, el método simplemente devuelve el valor del argumento, es decir, baseValue = baseReference.

Como se puede ver, al igual que en el ejemplo 1, baseValue es el valor de cadena primitivos 'some string'. Los pasos 3 y 4 son equivalentes a los pasos 3 y 4 en el Ejemplo 1.

Por lo tanto, tanto la referencia identificador str y la cadena literal 'some string' evaluar al mismo valor - el valor de cadena primitiva 'some string' - y se utiliza ese valor como baseValue para la nueva referencia que luego se invoca con (). Y dado que esta referencia tiene un valor base primitivo, se aplica la semántica definida al principio de mi respuesta.

+0

Ah-ha! Muchas gracias :) –

+0

Lo leí también, pero ¿cómo se relaciona literalmente una cadena con una referencia? ¿Qué es una referencia, o para ser más precisos, qué es un enlace de nombre resuelto? –

+0

'+ 1' - ¡Estaba buscando eso! –

1

por the reference literales se convierten en objetos:

literales de cadena (denotados por comillas simples o dobles) y las cuerdas regresaron de cuerdas llamadas en un contexto no constructor (es decir, sin usando la nueva palabra clave) son cadenas primitivas. JavaScript automáticamente convierte primitivas y objetos de cadena, por lo que es posible utilizar métodos de objetos de cadena para cadenas primitivas.

+0

Eso es lo que asumí, pero todavía no puedo encontrarlo en las especificaciones de ECMAScript. ¿Sabes si/dónde se describe este comportamiento allí? –

Cuestiones relacionadas