Pero lo que me gustaría hacer es tener la capacidad de pasar código arbitrario en JavaScript que se ejecutará en un ámbito particular, similar a cómo puede generar bloques en Ruby.

Toma esto por ejemplo:

function injectHook() {
    return function(block) {
        block();
    }
}

(function() {
    var a = 1;
    self.inject = injectHook();
})();

inject(function() {
    a++;
});

inject(function() {
    console.log(a);
});

El intento anterior no funcionará porque injectHook está definido en un alcance diferente y no podrá acceder a a.

El caso de uso principal aquí es para la depuración en vivo de código complejo.

Editar: Comencé un proyecto de GitHub en torno a esta pregunta

2
kristopolous 16 jul. 2011 a las 01:49

3 respuestas

La mejor respuesta

Esto debería funcionar:

function Inject(scope) {
    return 'Inject.' + scope + ' = ' + function(block) {
        return eval('(' + block + ')()');
    };
}

(function(){
    var a = 1;
    eval(Inject('ref'));
})();

Inject.ref(function(){ a++ });
Inject.ref(function(){ console.log('hello world', a) });
1
Peter Mortensen 21 oct. 2011 a las 15:41

En primer lugar, ¡gran pregunta! ¡Pasé al menos una hora tratando de encontrar una forma relativamente simple de implementarlo! Así es como abordé este problema:

Object.prototype.yield = function() {
    return "try { (" + this + ")() } catch (e) { console.log('**Context error** ' + e) }";
}

// first yielding block
var y1 = (Object(function() { 
    console.log('in y1, but still in the right context...');
    a++;
})).yield();

// second yielding block
var y2 = (Object(function() { 
    console.log('in y2, but still in the right context...');
    a = a + 5;
})).yield();

// voila
(function() {
    var a = 1;
    eval(y1);
    eval(y2);
    a--;
    eval(y2);
    console.log(a); // a == 11 now as expected
})();

// another block without an `a`
(function() {
    var b = 1;
    eval(y1); // gracefully fails
})();

Esto tiende a ser un poco más rubí en el sentido de que puede ceder en múltiples ubicaciones. No solo eso, sino que va un paso más allá al poder ceder ante bloques de código particulares (y1, y2, y3, etc.)

Espero que esta respuesta haya sido útil, ¡tu pregunta fue genial!

0
David Titarenco 16 jul. 2011 a las 02:46

Lo sentimos, pero eso no es posible en JavaScript. El código solo tiene acceso al alcance en el que se definió, y nunca puede obtener acceso a variables locales que están fuera del alcance.

La única forma de hacer algo similar es en un objeto:

function injectHook( obj ){
    return function( block ){
        block.apply( obj );
    }
}

function someClass(){
    this.a = 1;

    this.inject = injectHook( this );
}

var instance = new someClass;
instance.inject(function(){
    this.a++;
});

(Nota: las versiones anteriores de Firefox tienen un "error" en eval que hace posible lo que está intentando hacer, pero no es ampliamente posible).

0
Peter Mortensen 21 oct. 2011 a las 15:37