Estoy escribiendo un programa JavaScript que ejecuta el método de un objeto cada segundo. El método solo registra la propiedad del objeto "x", y luego le agrega uno.

La clase a partir de la cual se construye el objeto (llamado "jugador") se llama "Serpiente", y la propiedad "x" se define claramente como 0. Sin embargo, cuando se utiliza mi función "everyTick", se registra 'indefinido'.

Esto es muy extraño porque el método también registra player.x, que hace exactamente lo que debería. Pero cuando uso "esto" en lugar de "jugador", se registra indefinido. ¿Alguien puede explicar por qué y cómo solucionarlo?

Aquí está el código.

var FPS = 1;
var toBeRun = {
  func: function() {},
  index: 0
};

function tickFunction(func) {
  this.func = function() {
    func();
  }
}

function everyTick() {
  toBeRun.func()
  setTimeout(function() {everyTick();}, 1000 / FPS);
}

function forever(func) {
  toBeRun = new tickFunction(func);
}


function Snake() {
  this.x = 0;

  this.say = function() {
    console.log(player.x);
    console.log(this.x);

    player.x++;
    this.x++;
  }
}


var player = new Snake();

function start() {
  forever(player.say);
}

everyTick();

Es realmente confuso cuando lo ves así, así que aquí está en CodePen: http://codepen.io/Plygon/pen/jqJZvq?editors=0011

1
Polygon 11 may. 2016 a las 18:49

4 respuestas

La mejor respuesta

Esto se debe a que, cuando llama a forever, está pasando una referencia a la función player.say, pero esa referencia no está vinculada al objeto Snake. Entonces, cuando se llama a la función, no sabe qué es this.

Aquí hay un ejemplo simple:

function Person(name) {
    this.name = name;
    this.say = function () {
        return this.name;
    };
}

var p = new Person('Bob');
console.log(p.say()); // Bob
var fn = p.say;
console.log(p.say()); // undefined;

Lo que debe hacer es vincular la función al objeto. El método bind de la función devuelve una nueva función donde this está vinculada a cualquier objeto que pase a bind:

var boundFn = p.say.bind(p);
console.log(boundFn()); // Bob

Entonces, si lo haces:

forever(player.say.bind(player));

Debería poder utilizar this como espera dentro de la función say.

2
Andrew Burgess 11 may. 2016 a las 16:09

El problema es que su función pierde su contexto (el "esto").

La primera opción es guardar el "this" (var self = this).

Otra opción es usar bind, call o apply

Espero que esto :) ayude

1
f14ka 11 may. 2016 a las 16:18

El problema aquí es el scope de this intente esto:

function Snake() {
  this.x = 0;
  var parent =  this;

  this.say = function() {
    console.log(player.x);
    console.log(parent.x);

    player.x++;
    parent.x++;
  }
}

Aquí this dentro de this.say tiene un alcance local para dicha función, debe referirse a this desde la función parent.

1
Christopher Díaz 11 may. 2016 a las 16:09

El problema está aquí:

function start() {
  forever(player.say);
}

Cuando envía la función player.say para su uso en el ciclo, pierde la conexión con el objeto player.

Luego, en el lugar donde se llama a la función, realiza una simple invocación de función , pero no un método en el objeto reproductor :

function tickFunction(func) {
  this.func = function() {
    func(); // simple function invocation, which has `this` as `undefined`
  }
}

Para resolver el problema, simplemente actualice la función start a esta:

function start() {
  forever(player.say.bind(player));
}

bind() creará una nueva función con el contexto siempre conectado con la instancia player al llamarlo.

Este artículo ofrece muchos detalles sobre this palabra clave en JavaScript

1
Dmitri Pavlutin 22 may. 2016 a las 12:10