Scope Chains & Closures

O Escopo

  • Diferente das outras linguagens
  • Funções em JS

Global


var nome = 'Pablo Escobar';
function dizerNome() {
	return alert(nome);
}
console.log(nome);
					
O contexto global é a janela do navegador, acessível pelo objeto window (no browser) e global no Node.

Local


function figuraOculta() {
	var oculta = 'cachorro';
	return alert('Existe uma figura oculta, que é um ' + oculta + ' atras.');
}
alert(oculta); // erro
					
Ao contrário do escopo global, as variáveis dentro de uma função pertencem a função que engloba estas variáveis. Vale lembrar que em JavaScript, declarações sem var resultam na variável sendo jogada para o escopo global.

Declaração de variáveis

  • Regras de declaração de variáveis
  • Declarações com e sem "var"
  • Diferenças para com outras linguagens procedurais/orientadas a objetos
  • Variable hoisting
  • Function hoisting
  • "use strict";

function variableHoisting() {
	alert(nome);
	var nome = 'foo';
}
variableHoisting(); // o que acontece?
					

function variableHoisting() {
	alert(nome); 
	var nome = 'foo';
}
variableHoisting(); // não resulta em erro!
					
O código executado fica, na verdade, assim:

function variableHoisting() {
	var nome; // declaração movida para início do escopo
	alert(nome); 
	nome = 'foo';
}
variableHoisting(); // alerta undefined 
					
E se definirmos uma função dentro de uma função?

function digaNome(nome) {
	function capitalizar() {
		return nome[0].toUpperCase() + nome.substr(1).toLowerCase();
	}
	return alert(capitalizar());
}
digaNome("RoBeRTO"); // alerta "Roberto".
capitalizar() // erro!
				
As regras são as mesmas.
Vamos voltar ao último exemplo.

function digaNome(nome) {
	function capitalizar() {
		return nome[0].toUpperCase() + nome.substr(1).toLowerCase();
	}
	return alert(capitalizar());
}
				
Mesmo que eu não tenha declarado a variável nome dentro da função capitalizar, consigo usar este valor dentro da função "de dentro".

Closures

Uma função "se lembra" de variáveis definidas no contexto no qual ela própria é definida.

Este é o conceito de closures.

Funções são a única construção no Javascript que cria um novo escopo. Isto significa que é possível usar funções para encapsular valores, mantendo-os "privados".

(function() {
	var a = 0;
	console.log(a); // escreve "0" no console

	// a variável "a" só existe neste escopo.
	// Logo, deixará de existir assim que esta função acabar
})();
console.log(a); // erro
				
Note como é possível criar uma função e executá-la envolvendo-a em parênteses e chamando-a.

Funções são primeira classe

Podemos retorná-las de funções como se fossem valores.

function soma(a) {
	return function (b) {
		return a + b;
	}
}
var soma5 = soma(5);
var resultado = soma5(3);
alert(resultado); // alerta "8"
Este código só funciona porque a função retornada se "lembra" do valor passado na primeira chamada. Aliando closures a funções de primeira classe abre espaço para abstrações poderosas.

Module Pattern

Consiste em retornar um objeto de uma closure, mas manter dados sensíveis encapsulados de usuários do "lado de fora".

function Walker(seed) {
	var init = seed || 0;
	return {
		next: function() {
			return ++init;
		},
		cur: function() {
			return init;
		},
		back: function() {
			return --init;
		}
	};
}
var first = Walker(5);
console.log(first.next()); // 6
console.log(first.next()); // 7 
console.log(first.next()); // 8

var second = Walker();
console.log(second.next()); // 1
console.log(second.cur());  // 1
console.log(second.back()); // 0

// first.seed -> erro!
// second.seed -> erro!
Isso permite que possamos escolher quais métodos estão abertos para usuários da nossa função, e também regras para acesso das variáveis privadas.

E as scope chains?

É como a runtime engine JavaScript resolve o lookup de variáveis. Devemos visualizar o escopo como uma "cadeia".

Dúvidas?

Obrigado!

Twitter: @panuto_

Github: @nubunto


Nodeschool Campinas 2015