2010-06-15 19 views
11

Vi el ejemplo a continuación explicado en este site y pensé que ambas respuestas serían 20 y no las 10 que se devuelven. Escribió que tanto la coma como la asignación devuelven un valor, no una referencia. No entiendo muy bien lo que eso significa.¿Por qué el operador de asignación devuelve un valor y no una referencia?

Lo entiendo en relación con pasar variables a funciones o métodos, es decir, los tipos primitivos se pasan por valor y objetos por referencia, pero no estoy seguro de cómo se aplica en este caso.

También entiendo el contexto y el valor de 'esto' (después de la ayuda de stackoverflow) pero pensé que en ambos casos seguiría invocando como método, foo.bar() lo que significaría que foo es el contexto pero parece que ambos dan como resultado una función llamada barra().

¿Por qué es eso y qué significa todo esto?

var x = 10; 
var foo = { 
    x: 20, 
    bar: function() {return this.x;} 
}; 

(foo.bar = foo.bar)();//returns 10 
(foo.bar, foo.bar)();//returns 10 
+1

'(foo.bar = foo.bar)()' es tan ** ** yendo a mi lista de preguntas de la entrevista! ^.^ –

+0

@Ben: Not '(foo.bar, foo.bar)()'? ;-) Es decir, si quieres algo esotérico ... –

+9

@Ben - Avísame para quién trabajas, así que no cometas el error de venir a una entrevista :) – screenm0nkey

Respuesta

11

No tiene que ver con los valores frente a las referencias, tiene que ver con los valores this (a medida que se sospecha). En JavaScript, this se establece por completo por cómo se llama una función, no donde se define. Establece el valor this en una de tres maneras:

  1. Llame a la función a través de una propiedad de objeto usando la notación de acceso de propiedad, ya sea separada por puntos (obj.foo()) o notación entre corchetes (obj["foo"]()).
  2. Llame a la función a través de una propiedad de objeto utilizando una sentencia with (en realidad una variante del # 1, pero vale la pena llamar a cabo por separado, especialmente en lo que no es obvio a partir del código fuente)
  3. Uso los apply o call características de la instancia de función.

En los ejemplos anteriores, usted no está haciendo ninguna de ellas, por lo que terminan llamando a la función con el valor por defecto this, el objeto global, y así x proviene de allí más que de su objeto foo. Aquí hay otra manera de pensar acerca de lo que el código está haciendo:

var f = foo.bar; // Not calling it, getting a reference to it 
f();    // Calls the function with `this` referencing the global object 

Si no utilizan directamente una propiedad para hacer realidad la llamada (en lugar de recuperar el valor de la propiedad y luego hacer la llamada con ese), el manejo this no entra en acción.

+1

Tiene que ver con el Tipo de referencia, ya que tanto el operador de coma como la asignación simple usan internamente 'GetValue', y esto hace que se pierda el * objeto base * de la referencia. – CMS

+0

@CMS: Correcto, ese es el mecanismo subyacente detrás del comportamiento que describí. No pensé que era necesario profundizar en esas profundidades. (No hablé sobre el método [[Call]] o la propiedad [[Scope]], ya sea :-)). –

+0

@TJ y CMS: no dude en responder tan técnicamente como desee. Si no lo entiendo, te haré preguntas. También la respuesta de CMS tiene más sentido para mí ya que todavía no puedo entender por qué (foo.bar = foo.bar)() no equivale a foo.bar(). En tu ejemplo solo usaste f = foo.bar y eso tiene sentido para mí ya que f() es una llamada a función, pero si fue f.bar = foo.bar, entonces para mí eso equivale a f.bar() lo que significa f sería 'esto' – screenm0nkey

2

Lo está entendiendo mal.

Ambos ejemplos devuelven window 's x propiedad, ya que no se invocan directamente en foo.

El valor de la palabra clave this dentro de una función depende del contexto en el que se llamó a la función.

En una llamada a función normal (por ejemplo, myFunc()), this será el objeto global, que generalmente es window.
En una llamada a un método de objeto (por ejemplo, foo.bar()), this será el objeto sobre el que se invocó la función. (en este caso, foo)

Puede establecer el contexto explícitamente llamando al myFunc.call(context, arg1, arg2) o myFunc.apply(context, argArray).

Ambos ejemplos son invocaciones normales de una expresión que evalúa a foo.bar.
Por lo tanto, this es el window.

Ellos son equivalentes a

var func = (some expression); 
func(); 
+0

Entiendo todo lo que has escrito, incluido tu ejemplo pero en mi cabeza (foo.bar = foo.bar) todavía equivale a foo.bar() y no a bar (como me lo dices (que sé que es correcto). Tanto usted como CMS y TJ me han dado buenas respuestas, pero necesito entender cómo soy un poco simple. – screenm0nkey

8

Debe comprender cómo funciona el Reference Type interno.

Nota: Este no es un tipo de datos de idioma, es un mecanismo interno para manejar las referencias.

Una referencia se compone de dos elementos, el objeto base de y un nombre propiedad.

En su ejemplo, la referencia foo.bar tiene este aspecto.

// pseudo-code 
foo.bar = { 
    baseObject: foo, 
    propertyName: 'bar' 
} 

Tanto el comma operator y una simple assignment, se basan en conseguir el valor del nombre de la propiedad, que hace que el objeto base que se ha perdido, ya que se devuelve un solo valor (esto se hace a través de la operación interna GetValue) .

Esta es la forma en que funciona el GetValue funcionamiento interno:

// pseudo-code 
GetValue(V) : 
    if (Type(V) != Reference) return V; 

    baseObject = GetBase(V); // in your example foo 
    if (baseObject === null) throw ReferenceError; 

    return baseObject.[[Get]](GetPropertyName(V)); 
    // equivalent to baseObject[v.PropertyName]; 

Como se ve, un valor se devuelve, por lo que se pierde la referencia original.

Editar: La clave para entender por qué (foo.bar = foo.bar)(); no es equivalente a foo.bar(); se basa en el Simple Assignment operador, vamos a ver el algoritmo:

 
11.13.1 Simple Assignment (`=`) 
The production `AssignmentExpression` : 
       `LeftHandSideExpression` = `AssignmentExpression` 

is evaluated as follows: 

1. Evaluate LeftHandSideExpression. 

2. Evaluate AssignmentExpression. 

3.Call GetValue(Result(2)). 

4.Call PutValue(Result(1), Result(3)). 

5.Return Result(3). 

Básicamente cuando usted hace (foo.bar = foo.bar) la asignación real (Paso 4.) no tiene ningún efecto porque PutValue solo obtendrá el valor de la referencia y lo colocará de nuevo, con el mismo objeto base.

La clave es que los rendimientos operador de asignación (Paso 5) el valor obtenido en la Paso 3 y como he dicho antes en la pseudo-código GetValue, este método interno devuelve un valorque doesn' t realmente tiene un objeto base .

+0

Eso me enseñará. Le dije a T.J. Crowder lo hace tan técnico como quieras, pero ahora no puedo entenderlo. Tendré que pensar en lo que ha escrito y volver a verlo si está bien. – screenm0nkey

+0

@Nick, seguro, piénselo, se trata de un tipo completamente abstracto, que existe en la especificación exclusivamente para * fines expositivos *. Eso puede hacer que sea un poco difícil jadear. Lea sobre ello y sobre los métodos internos 'GetBase',' GetPropertyName' y 'GetValue', luego podemos ir un paso más allá y explicar cómo se usa el tipo de referencia para establecer el valor' this' cuando se realiza una función llamada. – CMS

+0

Gracias hombre. Tu comprensión de JavaScript es increíble. Me llevaré lo que has dicho y trataré de entenderlo, con algo de investigación también. En este punto, tengo que dar la respuesta correcta que le daré a TJ, no porque su respuesta haya sido mejor ya que todas las respuestas fueron excelentes, sino porque tengo que elegir una. Le haré +1 a usted y a los demás, ya que pensé que todas somos buenas y útiles respuestas. De nuevo, gracias por tu ayuda. – screenm0nkey

2

Puede ser útil pensar que el operador punto se comporta de manera similar a una instrucción with.Cuando se ejecuta foo.bar(), el resultado es el mismo que si se ejecutó:

with (foo) { 
    bar(); // returns 20 
} 

Esto ejecutará su función bar con foo superpuesta en la parte superior del objeto global, lo que le permite encontrar el x en foo y por lo tanto el retorno 20.

Si usted no llama bar de inmediato, sin embargo, como en (foo.bar = foo.bar), en su lugar se obtiene:

with (foo) { 
    bar; // returns "bar" itself 
} 

El resultado se pasa del paréntesis, produciendo una instrucción intermedia como <reference to bar>(), que no tiene operador de punto, por lo que no hay instrucción with, por lo que no hay acceso a foo, solo al valor global de x.

(El operador punto no lo hace realidad convertido a una declaración with, por supuesto, pero el comportamiento es similar.)

+0

Gracias, hombre - Eso me ayuda mucho. No había usado la declaración con antes pero me da algo para investigar. Entonces en este ejemplo (nick.foo.low.man = foo.man.tan.bar)() sería lo mismo que con (foo.man.tan) { bar; // devuelve "barra" en sí } – screenm0nkey

Cuestiones relacionadas