¿Qué problemas de programación comunes se resuelven mejor mediante el uso de prototipos y cierres?

8

Por mucho que entiendo ambos conceptos, no puedo ver cómo puedo aprovechar los cierres y prototipos de JavaScript, aparte de usarlos para crear bloques de clase instantaneos y / o encapsulados (lo que me parece más una solución que un activo) )

Es mucho más fácil enamorarse de otras características de JS, como las funciones como valores o la evaluación lógica de los no booleanos ...

¿Qué problemas de programación comunes se resuelven mejor mediante el uso de herencias y cierres propotípicos?

vemv
fuente
1
OO se resuelve mediante herencia prototípica. En JavaScript los prototipos son su mecanismo OO. Y los cierres son lo que usa para vincular el estado a una función. Te recomiendo que pruebes node.js o cualquier otra cosa que use mucho la lógica asincrónica, te enamorarás fácilmente de los cierres.
Raynos
Quizás no me hice lo suficientemente claro: ya soy consciente de lograr OO a través de prototipos, pero tiene que haber más usos de esa función, ¿no es así? Siéntase libre de poner algunos ejemplos de lógica asincrónica.
vemv
1
haces que parezca que lograr OO a través de prototipos es una hazaña menor, "seguramente los prototipos deben tener más usos que OO"
Raynos 21/11/11
2
@vemv: Los prototipos están ahí para darte OO. Período. Cualquier otra cosa es realmente solo abuso.
Jan Hudec
2
@vemv no son exóticos, la comunidad javascript solo hace un trabajo muy pobre en la enseñanza de prototipos. Ve a leer sobre los prototipos
Raynos

Respuestas:

5
  1. Los cierres son los que hacen que las funciones como valores sean útiles. Cuando pasa la función, es casi seguro que la necesita para tomar algún contexto. Que es exactamente lo que hacen los cierres.

  2. Los prototipos son solo una versión más simple de la herencia de clases. En lugar de tener instancias y clases (representadas por tipos especiales de instancias en lenguajes dinámicos de todos modos), solo tiene instancias y el prototipo es la clase (y su prototipo es la clase base). Entonces, básicamente resuelven los mismos problemas, solo los prototipos son más fáciles de implementar (es por eso que JavaScript los eligió), algo más difíciles de usar (bueno, simplemente falta de azúcar sintáctico) y para mejor o peor, más fáciles de abusar.

Jan Hudec
fuente
"cuando se pasan funciones, es casi seguro que necesites contexto" no realmente, mira C y sus punteros de función, no necesitan contexto. Generalmente, si pasa funciones a su alrededor, le da a estas funciones el contexto que necesitan. Los cierres seguros son una excelente manera de pasar el contexto, pero lejos de ser el único
Raynos el
"los prototipos son más fáciles de implementar (es por eso que JavaScript los eligió)" Eso es vago (y es muy probable que sea una declaración falsa) sin ningún tipo de referencia.
Raynos
@Raynos: en C a menudo pasamos el contexto a las funciones a través de un void* dataargumento para ser rechazado por la función llamada.
Kevin Cline
1
@Raynos: Estoy mirando los punteros de la función C y son muy difíciles de usar a menos que el código que los lleva pase a través de un vacío adicional * con contexto. En cuanto a los prototipos, obviamente son más fáciles de implementar, ya que solo tiene un tipo de objetos no primitivos y una forma uniforme de acceder a los miembros (y no está separado, por ejemplo, de los miembros de la clase y no tiene un comportamiento especial de los metaobjetos). Si bien no tengo una referencia de que los diseñadores de ECMAScript realmente eligieron prototipos por esta razón, el intérprete mínimo era un objetivo de diseño importante, por lo que es muy probable.
Jan Hudec
@ JanHudec Creo que es un par de órdenes de magnitud más probable que JavaScript tenga prototipos porque el lenguaje OO prototípico influyó en Brendan cuando escribió JavaScript.
Raynos
4

Los cierres no resuelven ningún problema de programación que no se pueda resolver sin ellos. Sin embargo, esto se puede decir para cualquier característica del lenguaje que no sea necesaria para la integridad de Turing, por lo que no significa mucho.

Piense en cómo debe reescribir cualquier código que use un cierre, para no usar un cierre. Probablemente le otorgaría a la función propiedades de cierre adicionales para que pueda mantener su estado entre llamadas. Lo cual sería simplemente copiar las variables que ya están dentro del alcance en propiedades del mismo nombre en la función, por lo que sería el tipo de ejercicio de escritura sin sentido que hace que quieras gritar en la pantalla "¿por qué no puede el estúpido compilador ( intérprete, lo que sea) resolver esto? Eso es lo que son los cierres, el estúpido compilador es lo suficientemente inteligente como para descubrirlo.

psr
fuente
Ayer me di cuenta por primera vez de cómo usar los cierres de una manera significativa: pueden hacer que la separación de las preocupaciones sea mucho más ligera: un componente enviaría una función anónima a otra, cuyo cuerpo hace referencia a un campo del que el segundo componente no es consciente. Gracias compilador basado!
vemv
@vemv: lo que podría hacer sin cierres haciendo un objeto que contenga la referencia al campo del que el segundo componente no es consciente y pasándolo al segundo componente, pero es un gran dolor en comparación con un cierre que bien podría decidir no hacerlo
psr
2

Los cierres son geniales para la lógica asincrónica.

Se trata principalmente de la organización del código para mí. Tener un montón de funciones locales para dividir lo que está haciendo el código es bueno.

create: function _create(post, cb) {
    // cache the object reference
    var that = this;

    function handleAll(err, data) {
        var rows = data.rows;

        var id = rows.reduce(function(memo, item) {
            var id = +item.id.split(":")[1];
            return id > memo ? id : memo;
        }, 0);
        id++;


        var obj = {
            title: post.title,
            content: post.content,
            id: id,
            // refer to the object through the closure
            _id: that.prefix + id,
            datetime: Date.now(),
            type: "post"
        }

        PostModel.insert(obj, handleInsert);
    }

    // this function doesn't use the closure at all.
    function handleInsert(err, post) {
        PostModel.get(post.id, handleGet);
    }

    // this function references cb and that from the closure
    function handleGet(err, post) {
        cb(null, that.make(post));
    }

    PostModel.all(handleAll);
}

Aquí hay otro ejemplo de cierre

var cachedRead = (function() {
    // bind cache variable to the readFile function
    var cache = {};

    function readFile(name, cb) {
        // reference cache
        var file = cache[name];
        if (file) {
            return cb(null, file);
        }

        fs.readFile(name, function(err, file) {
            if (file) cache[name] = file;
            cb.apply(this, arguments);
        });
    }

    return readFile;
})();

Y otro ejemplo

create: function _create(uri, cb, sync) {
    // close over count
    var count = 3;

    // next only fires cb if called three times
    function next() {
        count--;
        // close over cb
        count === 0 && cb(null);
    }

    // close over cb and next
    function errorHandler(err, func) {
        err ? cb(err) : next();
    }

    // close over cb and next
    function swallowFileDoesNotExist(err, func) {
        if (err && err.message.indexOf("No such file") === -1) {
            return cb(err);
        }
        next();
    }

    this.createJavaScript(uri, swallowFileDoesNotExist, sync)

    this.createDocumentFragment(uri, errorHandler, sync);

    this.createCSS(uri, swallowFileDoesNotExist, sync);
},

La alternativa al uso de cierres es curvar variables en funciones usando f.bind(null, curriedVariable).

Sin embargo, en general, la lógica de programación asincrónica utiliza devoluciones de llamada y el estado de manipulación en las devoluciones de llamada depende del currículum o los cierres. Personalmente prefiero los cierres.

En cuanto a los usos de la herencia prototípica, ¿permite OO? ¿La herencia prototípica realmente necesita hacer más que eso para que se considere "útil"? Es una herramienta de herencia, permite la herencia, eso es lo suficientemente útil.

Raynos
fuente