Fondo

Quiero una función que realice un seguimiento de su propio estado:

var myObject = {

    myFunction: function () {

        var myself = this.myFunction;
        var firstTime = Boolean(!myself.lastRetry);

        if (firstTime) {

            myself.lastRetry = Date.now();
            return true;
        }

        // some more code

    }
}

El problema con el código anterior es que el valor de this dependerá del sitio de la llamada a la función. Quiero que la función pueda referirse a sí misma sin usar:

  • myObject.myFunction
  • .bind()
  • .apply()
  • .call()

Pregunta

¿Es posible dar a una función este tipo de autoconciencia independiente de su sitio de llamada y sin ninguna ayuda de referencias externas?

4
rabbitco 12 may. 2016 a las 16:12

3 respuestas

La mejor respuesta

Si desea almacenar ese estado en la instancia de la función, asigne un nombre a la función y use ese nombre dentro de ella:

var myObject = {

    myFunction: function theFunctionName() {
    //                   ^^^^^^^^^^^^^^^--------------------- name
        var firstTime = Boolean(!theFunctionName.lastRetry);
    //                           ^--------------------------- using it
        if (firstTime) {

            theFunctionName.lastRetry = Date.now();
    //      ^------------------------------------------------ using it
            return true;
        }

        // some more code

    }
};

Lo harías siempre que quieras usar una función de forma recursiva también. Cuando le da un nombre a una función de esa manera (poniendo el nombre después de function y antes de (), ese nombre está dentro del alcance dentro del código de la función. (No está dentro del alcance del código que contiene la función si es una expresión de función, pero lo es si es una declaración de función. La suya es una expresión).

Esa es una expresión de función con nombre (donde anteriormente tenía una expresión de función anónima ). Es posible que escuche advertencias sobre NFE, pero los problemas que varias implementaciones de JavaScript tuvieron con ellos están esencialmente en el pasado. (IE8 todavía los maneja incorrectamente, sin embargo: Más en esta publicación en mi blog .)

Sin embargo, puede considerar mantener ese estado en algún lugar privado, a través de un IIFE:

var myObject = (function(){
    var lastRetry = null;
    return {
        myFunction: function() {
            var firstTime = Boolean(!lastRetry);
            if (firstTime) {

                lastRetry = Date.now();
                return true;
            }

            // some more code

        }
    };
})();

Ahora, nada fuera de esa función anónima externa puede ver lastRetry en absoluto. (Y no tiene que preocuparse por IE8, si es compatible con los usuarios obstinados de XP. :-))


Nota al margen: El operador unario ! siempre devuelve un valor booleano, por lo que su

var firstTime = Boolean(!theFunctionName.lastRetry);

... es exactamente equivalente a:

var firstTime = !theFunctionName.lastRetry;

... pero con una función adicional innecesaria. (No es que duela nada).

8
T.J. Crowder 12 may. 2016 a las 13:27

Por supuesto que puedes, simplemente dale a tu función una representación interna con nombre y se puede referir a sí misma desde allí. Por ejemplo...

var obj = {
    doThings:function doThingsInternal(arg1, arg2) {
        console.log(arg1, arg2);
        for (var arg in doThingsInternal.arguments) {
            console.log(arg);
        }
    }
};

obj.doThings('John', 'Doe');
0
ManoDestra 12 may. 2016 a las 13:19

Podría usar un Cierre simple, si no está demasiado decidido a mantener el conocimiento de la existencia del estado dentro de la función. Pero supongo que no quieres eso. Otra forma de hacer esto podría ser cambiar la función en sí en la primera llamada. ¡Beneficios, no se necesitan / menos variables de estado y no hay controles costosos en llamadas posteriores! -

    var myObject = {

    myFunction: function () {
       // Whatever you wanna do on the first call...
       // ...
       // And then...
       this.myFunction = function(){
         // Change the definition to whatever it should do
         // in the subsequent calls.
       }
       // return the first call value.
    }
};

Puede extender este modelo a cualquier estado cambiando la definición de la función según su estado.

0
hazardous 12 may. 2016 a las 13:24