Variáveis declaradas no contexto uma função JavaScript, como acessá-las?

JavaScript é tão dinâmico que podemos criar soluções que não existem na linguagem.

Até onde sabemos não é possível acessar variáveis declaradas no contexto de uma função.

Como funciona o escopo de variável em JavaScript

Em JavaScript temos dois tipos de escopo de variável, local e global. Toda variável declarada sem a instrução var será definida no contexto do objeto global ( window nos browsers ) e também será global se for definida com a instrução var no contexto global.

Ou seja:

var a = 1;
b = 2;

function doNothing() {
    var c = 3;
    d = 4;
};

doNothing();

console.log(a); // global
console.log(b); // global
console.log(c); // local ReferenceError c is not defined
console.log(d); // global

Funções construtoras retornam objetos

Funções construtoras retornam objetos contendo as propriedades e métodos definidos:

function Car(){
    var secretKey = '#$(**dwd73451#)&@!';
    this.type = 'flex';
    this.km = 0;
}

Car.prototype.run = function(km){
    this.km+=km;
    return this.km;
}

var golf = new Car();
console.log(gol); // Car { type="flex", km=0, run=function()}

Não temos como acessar a variavel secretKey, porem se implementarmos um método dentro da função construtora Car o mesmo terá acesso a variável secretKey:

function Car(){
    var secretKey = '#$(**dwd73451#)&@!';
    this.type = 'flex';
    this.km = 0;
    this.getSecretKey = function(){
        return secretKey
    }
}
var golf = new Car();
golf.getSecretKey(); // #$(**dwd73451#)&@!

Pelo que vimos aqui os requesitos seriam:

  • A função deveria ser usada como um construtor
  • Implementar um método que retornasse as variáveis

Implementando um novo método no objeto Function

Function.prototype.expose = function( private ){
    
    // this se refere a função atual
    
    // propriedade que contera a instancia da função mofificada
    this.fn = this.fn || undefined;

    if ( this.fn ){
        // se já houver uma instancia apenas chama o método get
        return this.fn.get( private )
    };
    
    // função que sera adicionada a a função modificada para retornar as variáveis
    var get = function( v ){
        return eval( v );
    };
    
    // this.toString() retorna o código fonte da função
    var fnSource = this.toString().match( /function.+?\((.*)\).+?\{([\s\S]*)\}/ );
    
    // aqui está o segredo, criar uma nova função usando o objeto Function
    // passando a string do código fonte e adicionando o novo método get
    var F = new Function( fnSource[1],'this.get='+get+';'+fnSource[2] );
    
    // usamos F como construtor e armazenamos uma instância em this.fn para futuros acessos
    this.fn = new F;
    
    // Function.prototype.expose retorna o resultado do método get
    return this.fn.get(private);
}

Alguns testes:

var fn = function() {
    var c = 1;
    return c;
};

var fn2 = function() {
    var a = 1;
    if( a > 0 ){
        a=10;
    }
    return a;
};

var config = function() {
    var url = 'http://10.42.43.100';
    var privateKey = '#aˆ&$DU)DI)RQ#@#$%ˆFHGFOIJE%$#$#%(';
};

console.log( fn.expose('c') );
console.log( fn2.expose('a') );
console.log( config.expose('privateKey') );

Conclusões

Como vimos é possível quebrar o escopo de uma variável em JavaScript, talvez de uma maneira não muito elegante ( eval, toString ) e com algumas limitações de uso.

Sinceramente nunca precisei usar esse artificio, essa solução (baseada na referência abaixo) foi apenas para demonstrar essa curiosidade da linguagem.

Referências

blog comments powered by Disqus