Sei sulla pagina 1di 37

JavaScript Patterns (2010)

Cap 01 introduo e contexto


Design Patter, no contexto de desenvolvimento de software um modelo de soluo comum para
problemas recorrentes.

Inicialmente os Design Patters foram elaborados por profissionais que usavam linguagens
fortemente tipadas, como C++ e Java. Mas no caso de linguagens dinmicas, como PHP ou
JavaScript, a melhor abordagem aquela que no restringe a tecnologia, mas a incrementa. Sem
tcnicas verbosas ou a criao de estruturas desnecessrias apenas para enquadrar a linguagem no
Padro usado.

Coding patterns, uma abordagem interessante, voltada para javascript e que reune boas prticas de
programao relacionadas com as caractersticas da linguagem, como funes, objetos literais e etc.

Antipattern uma um modelo de soluo comum, mas quando usado de forma inadequada, pode
causar mais problemas que o esperado. Isso visto mais claramente nos casos em que necessrio
deixar comentrios explicando o cdigo.

JSLint, programa criado para emitir alertas sobre prticas perigosas de codificao. Criada por
Douglas Crockford, pode ajudar a evitar futuros problemas de cdigo.

Cap 02 Boas prticas de cdigo


Cdigo fcil de manter exige:
1. Legibilidade
2. Consistncia
3. Pevisibilidade
4. Parece que foi escrito por quem est lendo
5. Est documentado

Javascript usa funes para gerenciar seu escopo. Sendo assim, variaveis fora de funes so
consideradas globais, mas acessveis pelo objeto Window. Portanto, o ponto mais global do
javascript o objeto Window e atravs dele que podemos acessar todas as nossas variveis globais.

Window.myvar
window[myvar]

Qual o problema com globais? Risco de coliso. Compartilham as variveis com todo o escopo de
cdigo do sistema, assim como bibliotecas de terceiros, scripts de parceiros, cdigos de
rastreamento como analytics, widgets.

Coliso, duas variveis com o mesmo nome, mas com propostas de uso diferentes.

function soma(x, y) {
// antipattern result pode ser um global
result = x + y;
return result;
}

Sempre declare variveis com var ou let para informar o escopo delas.
Problemas com Hoisting

// antipattern
myname = "global"; // global variable
function func() {
alert(myname); // "undefined"
var myname = "local";
alert(myname); // "local"
}
func();

Voc poderia esperar que o primeiro alert mostre o valor global, mas ele mostra undefined. Por
causa da declarao local var myname = local. Sempre declare suas variveis com var.

Problemas com loops

for (let index = 0; index < lista.length; index++) {


// faz alguma coisa...
}

O problema com o padro acima, que muitas listas calculam length em tempo de execuo,
tornando cada incremento do loop numa nova contagem e deixando o cdigo lento. Para evitar isso,
use.

let total = lista.length;


for(let index = 0; index < total; index++) {
// faz alguma coisa mais rpido...
}

Sempre faa um cache de length e totais de listas.

Em loops, evite usar ++ ou . Ele realizam truques internos excessivos para realizar o incremento
mais atribuio. Em vez disso, use i += 1 ou i = i + 1.

Problemas com for-in

For-in usado apenas para objetos, para navegar por suas propriedades. Contudo, ele pode
percorrer cadeias de prototypes em herana de objetos ou em objetos derivados. Para eviar esse
problema e garantir a navegao apenas pelas propriedades do objeto em questo use sempre
hasOwnProperty(prop) para filtrar as propriedades.

for (let prop in obj) {


if (obj.hasOwnProperty(prop)) { // um erro fazer um loop sem isso
console.log(prop, : , obj[prop]);
}
}
Evite criar propriedades ou metodos personalizados em Objetos padres

Nem todos os profissionais conhecem completamente todos os objetos padres, ento eles podem
pensar que a sua funcionalidade uma implemntao do navegador e us-la de forma inadequada.
Ou ento, experar um comportamento padro, mas descobrir da pior forma que aquele mtodo foi
sobrescrito por outra pessoa.

Prefira usar === a ==, a converso implcita pode causar dores de cabea para que l seu cdigo.
No d para saber se == foi intencionalmente usado ou se foi displicncia do programador. Ento
realize o casting e use typeof antes de comparaes.

setTimeout(myFunc(1, 2, 3), 1000); // mais lento que


setTimeout(function() {
myFunc(1, 2, 3);
}, 1000);

O primeiro cdigo exige uma avaliao do interpretador, como a execuo de um eval().

Convenes e cdigo
1. Identao (2 ou 4 espaos) nunca tabs
2. Sempre use chaves em todos os blocos, mesmo em loops com apenas uma instruo.
3. Sempre abra as chaves na mesma linha das instrues.

// warning: unexpected return value


function func() {
return // e necessrio abrir a chave na mesma linha que return
{
name: "Batman"
};
}

Documente seu cdigo com JSDoc - https://github.com/jsdoc3/jsdoc

/**
* Reverse a string
*
* @param {String} input String to reverse
* @return {String} The reversed string
*/
var reverse = function (input) {
// ...
return output;
};

Cap 03 Objetos literais e construtores


Explica como criar objetos de forma literal usando {}. muito bla bla bla.
Interessante. O que acontece se chamar uma funo construtora sem o new? A funo no cria
um novo objeto, ele apenas atribui mtodos e propriedades ao objeto mais global do javascript
window. Vlido at O ES5 sem strict.
function Cat () {
this.catName = Black Cat;
}
var good_cat = Cat();
console.log(typeof good_cat); // undefined
console.log(window.catName); // Black Cat

Lembre-se de usar sempre construtores (funes) como CamelCase e nomes de mtodos/funes


como camelCase. Ok?
Outra tcnica interessante de funes construtoras. Quase uma fbrica.
function Cat () {
var that = {};
that.nome = Black Cat;
return that;
}

function Dog () {
return {
nome: Biggle
};
}

Uma coisa interessante, no ES5 arrays possuem typeof == object.


var a = [1, 2, 3];
console.log( typeof a ); // object
console.log(a.constructor === Array); // true

Uma diferena bsica entre JSON e objetos literais que JSON no suporta funes ou expresses
regulares. Outra diferena importante que as propriedades num JSON vlido esto sempre entre
, enquando objetos literais permitem o uso de nomes de propriedades sem .
Sempre que puder, use JSON.parse e JSON.stringfy.
Apesar dos tipos primitivos em JavaScript terem um comportamento parecido com objetos, eles no
so objetos. Tentar criar propriedades diretamente neles no funciona.

// primitive string
var greet = "Hello there";
// primitive is converted to an object
// in order to use the split() method
greet.split(' ')[0]; // "Hello"

// attemting to augment a primitive is not an error


greet.smile = true;
// but it doesn't actually work
typeof greet.smile; // "undefined"

Isso legal!!!!
O comando throw pode lanar qualquer tipo de objeto como erro. Assim voc pode criar um objeto
lilteral e lan-lo com mais informaes para recuperar no catch.
try {
// something bad happened, throw an error
throw {
name: "MyErrorType", // custom error type
message: "oops",
extra: "This was rather embarrassing",
remedy: genericErrorHandler // who should handle it
};
} catch (e) {
// inform the user
alert(e.message); // "oops"
// gracefully handle the error
e.remedy();
}

Cap 04 Funes
A principal fora das funes em javascript que elas so objetos, ento elas podem manifestar as
seguintes caractersticas:
So dinmicas: Podem ser criadas dinamicamente em tempo de execuo.

So atribuveis: Podem ser atribudas variveis, podem ter sua referncia copiada para
outras variveis, podem ser passadas como argumentos de outras funes ou mesmo ser o
resultado retornado por uma funo, e em casos especiais, podem ser deletadas.
So objetos: Possuem propriedades e mtodos.

Possuem escopo prprio: Quando usamos var para declarar uma varivem dentro de um if,
um while, um for ou qualquer outra estrutura de controle, no estamos criando uma varivel
local. Var cria variveis com base no escopo de funes, e se no est dentro de uma
funo, ento est no escopo global. IMPORTANTE!!!!
Declaraes internaas: Funes podem ser definidas dentro de outras funes, criando um
escopo privado para o mundo externo.
ATENO!!!!
Toda funo definida em JavaScript avaliada antes da execuo de qualquer cdigo. Sendo assim,
ela sempre ser executada normalmente. Independente da definio dela estar antes ou depois do
cdigo que a utiliza.
O problema acontece quando criamos uma funo annima atribuindo ela para uma varivel. Em
casos assim, a funo se comporta como varivel. Caso sua execuo seja realizada antes de sua
definio, ela emite um TypeError.
function main() {

console.log(typeof foo); // function


console.log(typeof bar); // undefined

foo(); // local function


bar(); // typeerror: bar is not a function

function foo() {
console.log(local function);
}

var bar = function() {


console.log(local bar);
}

main();

O padro callback
Basicamente um padro que cria uma funo esperando outra funo como parmetro. A funo
principal pode executar qualquer bloco de cdigo e chamar a funo passada como parmetro no
momento apropriado.
Quando quiser trabalhar com objetos no padro callback, voc pode passar o objeto como
parmetro junto com a indicao da funco num segundo parmetro.
var findNodes = function(callback, obj) {
if (typeof callback == string) {
callback = obj[callback];
callback(parametro);
}

if (typeof callback == function) {


callback.call(obj, parametro);
}
}

// pode ser executado assim


findNodes(obj, metodo_de_obj);
funcNodes(obj, boj.metodo);

O padro callback pode ser usado com os seguintes objetivos:


1. Returning: Retornar uma funo especializada, arrays ou objetos.
2. Callback: Executar uma funo ou mtodo de objeto internamente, no momento
apropriado.
3. Redefinio: Executar uma funo internamente, modificando-a para uma posterior
execuo.
Exemplo de redefinio
var showMe = function() {
console.log(boo!);
showMe = function() {
console.log(Double boo!!!);
};
};

showMe(); // boo!
ShowMe(); // Double boo!!!
Immediate functions
Tcnica criada para inicializar ou configurar elementos da pgina diminuindo o custo de memria.
Como todo o cdigo de inicializao colocado no escopo local, a funo definida, executada e
tudo o que no mais necessrio liberado da memria do navegado.
(function(param) {
var privateVar = minha privada;
var date = new Date(); // depois sera liberada da memria

console.log(ola! + param + !);


console.log(date);
}(eduardo));

Immediate object initialization


Da mesma forma que Immediate functions, ele serve para evitar lixo no escopo global e diminuir o
consumo de memria no navegador. Mas dessa vez, faz uso de objetos.
({

// here you can define setting values


// a.k.a. configuration constants
maxwidth: 600,
maxheight: 400,

// you can also define utility methods


gimmeMax: function () {
return this.maxwidth + "x" + this.maxheight;
},

// initialize
init: function () {
console.log(this.gimmeMax());
// more init tasks...
}

}.init())

Se quiser manter o objeto para uso posterior, basta introduzir o comando return no final de init()
init-time branching (load-time branching)
Padro utilizado para inicializao de configuraes durante o carregamento do cdigo javascript,
evitando retestar condies, diminuindo o consumo de memria e tempo de resposta do cdigo.
Imagine que voc est trabalhando com javascript puro e precisa saber como anexar eventos a
elementos do DOM. Um programador comum faria isso:
var utils = {
addListener: function (el, type, fn) {
if (typeof window.addEventListener === 'function') {
el.addEventListener(type, fn, false);
} else if (typeof document.attachEvent === 'function') { // IE
el.attachEvent('on' + type, fn);
} else { // older browsers
el['on' + type] = fn;
}
},
removeListener: function (el, type, fn) {
// pretty much the same...
}
};
Note que apesar do objeto utils ser muito popular, ele executa os mesmos testes sempre que seus
mtodos forem executados, aumentando o tempo de execuo e consumo de memria para cdigos
mais complexos.
Em essncia, o que voc precisa fazer detectar o suporte do navegador uma nica vez, pois o
ambiente no muda durante a execuo do cdigo javascript, evitando todos os testes sempre que o
cdigo for necessrio.
// the interface
var utils = {
addListener: null,
removeListener: null
};

// the implementation
if (typeof window.addEventListener === 'function') {
utils.addListener = function (el, type, fn) {
el.addEventListener(type, fn, false);
};
utils.removeListener = function (el, type, fn) {
el.removeEventListener(type, fn, false);
};
} else if (typeof document.attachEvent === 'function') { // IE
utils.addListener = function (el, type, fn) {
el.attachEvent('on' + type, fn);
};
utils.removeListener = function (el, type, fn) {
el.detachEvent('on' + type, fn);
};
} else { // older browsers
utils.addListener = function (el, type, fn) {
el['on' + type] = fn;
};
utils.removeListener = function (el, type, fn) {
el['on' + type] = null;
};
}

Note que agora, o cdigo de teste executado apenas uma vez, diminuindo o tempo de resposta do
sistema e diminuindo o consumo de memria, pois no ser necessrio retestar o suporte do
navegador novamente.
Uma dica combinar esse padro com Immediate Function Invocation, eliminando recursos
globais no cdigo javascript.
Memorizao (funes)
Padro utilizado para realizar cache do resultado de um processamento. Assim, caso seja necessrio
realizar o mesmo processamento duas vezes, o resultado armazenado na primeira execuo e
apenas retornado na segunda execuo.
var myFunc = function () {
var cachekey = JSON.stringify(Array.prototype.slice.call(arguments)), result;

if (!myFunc.cache[cachekey]) {
result = {};
// ... expensive operation ...
myFunc.cache[cachekey] = result;
}

return myFunc.cache[cachekey];
};

// cache storage
myFunc.cache = {};

Objetos de configurao
um padro usado para trocar uma grande quantidade de parmetros de uma funo por um objeto.
O objeto pode ser um criado ou um objeto literal.
Fcil de ler e dar manuteno

Fcil de adicionar e remover parmetros (propriedades)

Fcil de utilizar parmetros opcionais e valores padres

No necessrio lembrar a ordem dos parmetros

Muito til para configurao de elementos DOM ou CSS

Desvantagens
Voc precisa lembrar os nomes das propriedades (parmetros)

Nomes de propriedades no podem ser minificados

Currying
Padro usado para quebrar a execuo de uma funo em etapas, armazenando parmetros para
evitar o envio deles repetidamente para a mesma funo.
// a curried add
// accepts partial list of arguments
function add(x, y) {
if (typeof y === "undefined") { // partial
return function (y) {
return x + y;
};
}
// full application
return x + y;
}

// test
typeof add(5); // "function"
add(3)(4); // 7
// create and store a new function
var add2000 = add(2000);
add2000(10); // 2010

Mesma demostrao acima, mas com argumentos infinitos.


function schonfinkelize(fn) {
var slice = Array.prototype.slice,
stored_args = slice.call(arguments, 1);
return function () {
var new_args = slice.call(arguments),
args = stored_args.concat(new_args);
return fn.apply(null, args);
};
}

// test

// a normal function
function add(x, y) {
return x + y;
}
// curry a function to get a new function
var newadd = schonfinkelize(add, 5);
newadd(4); // 9
// another option -- call the new function directly
schonfinkelize(add, 6)(7); // 13

05 Padres para criao de objetos

Namespace Pattern
Consistem em criar um objeto global com objetivo de conter toda a biblioteca em propriedades do
objeto global.
Vantagens:
1. Evita uso de variveis globais e suas fragilidades
2. Evita colises de nomes de variveis.
var MYAPP = MAYAPP || {}; // objeto global (namespace)
MYAPP.Parent = function() {};
MYAPP.Child = function() {};
MYAPP.some_var = 10;
MYAPP.modules = {};
MYAPP.modules.modulo_01 = {
person: {
myfunc: function() {},
myprop: myprop
}
};

Desvantagens:
1. Maior tamanho de arquivos. Pesa um pouco mais prevfixar toda uma biblioteca com um
namespace do tipo objeto.
2. Ter apenas um objeto global, signnifica que qualquer parte do cdigo pode alterar mtodos
ou propriedades do objeto global (namespace). Assim, todo o resto do cdigo usaria o estado
atualizado do objeto global logo em seguida. Isso pode ser tanto uma vantagem quanto uma
desvantagem tremenda, gerando comportamentos inesperados. Cuidado!
3. Quanto mais longo for o namespace, mais lento a execuo do cdigo. Uma vez que o
navegador ter que interpretar uma cadeia de objetos para executar um cdigo especfico,
isso pode ser trabalhoso para ele.
4. A medida que o cdigo cresce, preciso ter cuidado para no sobrescrever objetos, mdulos
ou outros namespaces. Por isso, sempre verifique se um mdulo ou objeto existe antes de
cri-lo no namespace.
A maneira mais segura de usar um namespace, e criando uma funo para gerenciar a criao e
atualizao do mesmo. Segue um exemplo:
// ---- mylib.js ----
var MYAPP = MYAPP || {};
MYAPP.namespace = function(ns) {
var parts = ns.split('.');
var parent = MYAPP;
var i = 0;

if (parts[0] == 'MYAPP') {
parts = parts.slice(1);
}

for(let index = 0; index < parts.length; index = index + 1) {


if (typeof parent[ parts[ index ] ] == 'undefined') {
parent[parts[index]] = {};
}
parent = parent[parts[index]];
}

return parent;
};

number = MYAPP.namespace('MYAPP.number');
number.soma = (a, b) => a + b;
number.mult = (a, b) => a * b;

date = MYAPP.namespace('MYAPP.date');
date.hoje = function() {
return new Date().toString();
};

// --- usando no main.js ----


var number = MYAPP.namespace(MYAPP.number);
console.log(number.soma(10,35));

Para trabalhar melhor com dependncias e escopos privados, a criao de namespaces pode ser
modificada para funcionar com functions.
// ---- app.js ----
var APP = APP || {};

APP.namespace = function(ns, module) {


var parts = ns.split('.');
var parent = APP;
var i = 0;

if (parts[0] == 'APP') {
parts = parts.slice(1);
}

for(let index = 0; index < parts.length; index = index + 1) {


if ((typeof parent[ parts[ index ] ] == 'undefined') && (module)) {
parent[parts[index]] = module();
}
parent = parent[parts[index]];
}
return parent;
};

// ---- date.js ----


APP.namespace('APP.date', function() {
var date = new Date();
return {
'hoje': date.toString(),
}
});

// ---- number.js ----


APP.namespace('APP.number', function() {
var privatevar = 10;
const PRIVATE_CONST = 35;

var date = APP.namespace('APP.date');

return {
'soma': (a, b) => a + b,
'mult': (a, b) => a * b,
'calc': (num) => num * PRIVATE_CONST,
'getDate': () => APP.date.hoje
};
});

// ---- main.js ----


// A sequncia de includes inportante
// para as dependncias funcionarem
//
// include app.js
// include date.js
// include number.js
var number = APP.namespace('APP.number');

console.log(number.soma(10, 35));
console.log(number.calc(10));
console.log(number.getDate());
console.log(APP);

Mtodos e propriedades privadas


Consiste em fazer uso de closures para envolver os getters e setters.
function Person() {
var name = 'Adonias'; // privada

this.getName = function() {
return name;
}

this.setName = function(val) {
name = val;
}
};

var pessoa = new Person();


console.log(pessoa.getName(), pessoa.name);
// adonias, undefined
pessoa.setName('Marcos');
console.log(pessoa.getName(), pessoa.name);
// marcos, undefined
Quando usar closures, nuca retorne um objeto com propriedades. Objetos sempre so passados por
referncia, permitindo que o usurio modifique dados privados.
function Person() {
var props = {
nome: 'Adonias',
idade: 35
};

this.getName = function() {
return props.nome;
}

this.setName = function(val) {
props.nome = val;
}

this.getProps = function() {
return props;
}
};

var pessoa = new Person();


var prop = pessoa.getProps();
console.log(pessoa.getName());
prop.nome = 'marcos';
prop.idade = 25;
console.log(pessoa.getProps());

Prottipos de membros privados


Cada vez que um objeto criado, os membros privados so criados e consomem bastante memria.
Para evitar esse tipo de problema, comum usar prototype para criar membros de um objeto com
variveis privadas, numa mistura de closures e prototype.
Note que esta tcnica similar ao LazyLoad de algumas linguagens como Java.
function Gadget() {
// private member, criado sempre que
// o construtor for chamado
var name = 'iPod';
// public function
this.getName = function () {
return name;
};

Gadget.prototype = (function () {
// private member
// criado apenas quando o mtodo for chamado
var browser = "Mobile Webkit";
// public prototype members
return {
getBrowser: function () {
return browser;
}
};
}());

var toy = new Gadget();


console.log(toy.getName()); // privileged "own" method
console.log(toy.getBrowser()); // privileged prototype method

Revelando mtodos privados com API pblica


Sinceramente no vi utilidade para isso, ele usa closures para atribuir mtodos um objeto global.
Seria mais simples retornar a API como objeto. Da mesma forma que realizado com mdulos.
var myarray;

(function () {

var astr = "[object Array]",


toString = Object.prototype.toString;

function isArray(a) {
return toString.call(a) === astr;
}

function indexOf(haystack, needle) {


var i = 0,
max = haystack.length;
for (; i < max; i += 1) {
if (haystack[i] === needle) {
return i;
}
}
return 1;
}

myarray = {
isArray: isArray,
indexOf: indexOf,
inArray: indexOf
};

}());

Padro mdulo
uma combinao de padres para poder quebrar seu javascript em vrios pedaes de cdigo. O
mais recomendado para construo de bibliotecas.
Namespaces

Invocao Imediata de Funes

Membros privados e acesso privilegiado

Delcarao de dependncias

O primeiro passo criar um namespace usando o pado abaixo:


var MYAPP = MYAPP || {};
MYAPP.namespace = function(ns) {
var parts = ns.split('.');
var parent = MYAPP;
var i = 0;

if (parts[0] == 'MYAPP') {
parts = parts.slice(1);
}
for(let index = 0; index < parts.length; index = index + 1) {
if (typeof parent[ parts[ index ] ] == 'undefined') {
parent[parts[index]] = {};
}
parent = parent[parts[index]];
}

return parent;
};

Depois disso, criar um closure com membros privados e acesso privilegiado. Retornando uma API
publica para ser consumida por seus usurios.
MYAPP.namespace(MYAPP.utilities.array);
MYAPP.utilities.array = (function(dom, event) {
var private_var = 10;

function private_function(dom, private_var) {


return private_var * parseInt(dom.getelement(#mydom).textContent);
};

return {
func_01: function() {
dom.getElement(#mydom);
return dom.myfunc();
},

func_02: function() {
event.emit(event, private_var);
return private_var;
},

func_03: private_function
};
})(global_obj_01, global_obj_02);

Sandbox
Uma implementao de mdulos utilizado pelo Yahoo UI (YUI), utilizando uma funo construtora
para inicializar modulos, adicionar dependncias e executar o cdigo de uma determinada pgina.
uma implementao interessante e diferente do que estou acostumado.
function Sandbox() {
// trasnforma arguments em array
var args = Array.prototype.slice.call(arguments);
// o ltimo argumento o callback
var callback = args.pop();
// modules podem ser passados como array ou parametros individuais
var modules = (args[0] && typeof args[0] == 'string') ? args : args[0];
var i = null;

// certifica-se de que a funo executada como um construtor


if (!(this instanceof Sandbox)) {
return new Sandbox(modules, callback);
}

// quando necessrio
// voc pode adicionar mais
// propriedades ao this
this.a = 1;
this.b = 2;
// adiciona os mdulos no this
// quando usa '*', adiciona todos os mdulos ao this
if (!modules || modules == '*') {
modules = [];
for (i in Sandbox.modules) {
if (Sandbox.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}

// inicializa os mdulos requeridos


for (i = 0; i < modules.length; i = i + 1) {
Sandbox.modules[ modules[i] ](this);
}

// executa o callbak
callback(this);
}

Sandbox.prototype = {
name: 'My application',
version: '1.0',
getName: function() {
return this.name;
}
};

Sandbox.modules = {};

Sandbox.modules.dom = function(box) {
box.getElement = function() { console.log('getElement'); };
box.getStyle = function() { console.log('getStyle'); };
box.foo = function() { console.log('foo'); };
};

Sandbox.modules.event = function(box) {
box.attach = function() { console.log('attach'); };
box.dettach = function() { console.log('detach'); };
};

Sandbox.modules.ajax = function(box) {
box.get = function() { console.log('get'); };
box.post = function() { console.log('post'); };
};

// os mtodos dos mdulos so adicionados


// ao objeto box. Mesclando event e dom
// juntos no mesmo objeto.
Sandbox('event', 'dom', function(box) {
box.getElement();
box.attach();
console.log(box);
});

Sandbox('ajax', 'dom', function(box) {


box.getElement();
box.get();
console.log(box);
});
Propriedades e mtodos estticos
Consiste em executar mtodos sem precisar instanciar um objeto. Alm disso, tambm deve ser
possvel executar o mesmo mtodo em cada instncia do mesmo objeto. Para isso, usamos
prototype de um objeto javascript.
// constructor
var Gadget = function (price) {
this.price = price;
};
// a static method
Gadget.isShiny = function () {
// this always works
var msg = "you bet";

if (this instanceof Gadget) {


// this only works if called non-statically
msg += ", it costs $" + this.price + '!';
}

return msg;
};

// a normal method added to the prototype


Gadget.prototype.isShiny = function () {
return Gadget.isShiny.call(this);
};

console.log(Gadget.isShiny()); // you bet


var gad = new Gadget('499.99');
console.log(gad.isShiny()); // you bet, it costs $499.99!

Para compartilhar membros privados entre instncias, use closures para definir um construtor e sua
interface via prototype. Isso muito legal!!!
// constructor
var Gadget = (function () {
// static variable/property
var counter = 0,
NewGadget;
// this will become the
// new constructor implementation
NewGadget = function () {
counter += 1;
};
// a privileged method
NewGadget.prototype.getLastId = function () {
return counter;
};
// overwrite the constructor
return NewGadget;
}()); // execute immediately

var iphone = new Gadget();


iphone.getLastId(); // 1
var ipod = new Gadget();
ipod.getLastId(); // 2
var ipad = new Gadget();
ipad.getLastId(); // 3

Constantes de objetos
Mecanismo similar ao Math.MAX_VALUE.
A maneira mais fcil de fazer isso definindo uma varivel esttica no prprio objeto. Mas ainda
assim, um usurio descuidado pode alterar o valor da varivel.
var widget = function() {
// . definio do objeto
};
widget.MAX = 10;
widget.MIN = 5;

Para resolver isso, basta usar um objeto constant que possui os atributos define, isDefined e get. Ao
criar um constante com define a constante no poder mais ser redefinida.
var constant = (function() {
var constants = {};

return {

define: function(name, val) {


if (this.isDefined(name)) {
return false;
}

constants[name] = val;
return true;
},

isDefined: function(name) {
if (constants.hasOwnProperty(name)) {
return true;
}
return false;
},

get: function(name) {
return constants[name];
}

};
})();

console.log('isDefined', constant.isDefined('MAX_WIDTH')); // false


console.log('define', constant.define('MAX_WIDTH', 345)); // true
console.log('isDefined', constant.isDefined('MAX_WIDTH')); // true
console.log('get', constant.get('MAX_WIDTH')); // 345
console.log('define', constant.define('MAX_WIDTH', 487)); // false
console.log('get', constant.get('MAX_WIDTH')); // 345

Cadeia de execuo
Consiste em retornar o this na execuo dos mtodos, possibilitando o encadeamento de funes
uma aps a outra, at que o resultado final seja alcanado.
var obj = (function() {
var val = 0;
return {
increment: function() {
val = val + 1;
return this;
},
add: function(num) {
val = val + num;
return this;
},
mult: function(num) {
val = val * num;
return this;
},
val: function() {
return val;
}
}
})();

console.log(obj.increment().add(10).mult(3).val());

Apesar de ser amplamente utilizado em Jquery, esse recurso no recomendado por prticas
SOLID. Como temos muitas execues numa nica linha, caso aparea um bug nessa linha,
vai ser difcil descobrir onde o problema est acontecendo. Se voc ama codificar, no use essa
tcnica.

Padro Method
Criado por Douglas Corckford, consiste numa evoluo da cadeia de execuo, implementando
um novo mtodo no prototype de Function, fazendo com que todos os mtodos criados com esse
novo recurso retornem o this por padro.
if (typeof Function.prototype.method !== 'function') {
Function.prototype.method = function(name, implementation) {
this.prototype[name] = implementation;
return this;
};
}

var Person = function(name) {


this.name = name;
}.method('getName', function() {
return this.name;
}).method('setName', function(name) {
this.name = name;
return this;
});

var pessoa = new Person('eduardo');


console.log(pessoa.getName());
console.log(pessoa.setName('marcos').getName());

Novamente, muito cuidado. Um erro na linha de execuo pode ser difcil de depurar e
corrigir.
06 Reuso de Cdigo
Prefira sempre composio herana.
Herana clssica, faz uso de classes e objetos no sentido geral, como java e c++ que so fortemente
tipados.
Herana moderna, no segue exatamente o conceito de classes e objetos, uma vez que linguagens
como javascript no possuem sintaxe nem conceitos de objetos restritos. Objetos so apenas um
conjunto de pares chave-valor. Ainda assim, segue a premissa do Gof, prevfica composio
herana.
Padres clssicos em JavaScript
Na herana usandp Prototype, existe lentido na busca e execuo dos membros. Caso um mtodo
ou propriedade no seja encontrada no prototype do objeto, a busca ocorre nos prototypes filhos at
que o membro procurado seja encontrado e depois executado.
Alm disso, caso seja necessrio passar parmetros no construtor, os parmetros funcionam apenas
para as propriedades imediatas. Assim, se for necessrio passar parmetros para as propriedades do
prototype filho o construtor do filho ter que ser executado, criando um objeto filho dentro do pai.
Isso gera mais consumo de memria e lentido na execuo do javascript. (mas isso falando do
JavaScript vanila, no ES6 isso precisa ser investigado.)
O que espera-se da herana? Apenas a cpia dos membros de um objeto para outro, seja atravs de
um construtor ou qualquer outra coisa.
ES5 criou um padro de herana de prototypes usando a funo Object.create().
Var child = Object.create(parent);
O captulo inteiro sobre padres de herana e no sobre reuso de cdigo. O reuso se encontra no
fato de que o prototype reutilizado por objetos filhos. O padro de reuso mais simples o do
Object.create(parent) do ES5. Achei particularmente chato. Mais tarde talvez eu volte para
estudar isso com mais detalhes, mas ainda assim achei desnecessrio j que ES5 tem
Object.create e ES6 tem extends. Os padres estudados so:
Herana padro
Copia o construtor do filho para o pai via funo inherited(child, parent).
function heranca(child, parent) {
child.prototype = new parent();
}

function Parent() {
this.name = 'pai';
this.say = function() {
console.log('oi mundo!');
};
}

function Child() {
this.age = 15;
}
heranca(Child, Parent);
var child = new Child();
child.say();
console.log(child.name);
console.log(child.age);

Desvantagens:
Pesada busca na cadeia de prototypes

No passa argumentos para o prototype pai ou av

Construtor emprestado
Alugar um constructor, usa o construtor do pai passando o objeto filho como contexto dele. Assim,
podemos passar argumentos do filho para o pai e ainda realizar herana mltipla fcilmente.
function Child(name) {
Parent.apply(this, arguments);
}
//------
function Cat() {
this.legs = 4;
this.say = function () {
return "meaowww";
}
}

function Bird() {
this.wings = 2;
this.fly = true;
}

function CatWings() {
Cat.apply(this);
Bird.apply(this);
}

var jane = new CatWings();


console.dir(jane);

Aqui existe o risco de um filho sobrescrever um prototype pai, uma vez que os prototypes so para
o filho.
Construtor emprestado + atribuio de prototype (rent and set prototype)
Prototype compartilhado (shared pototype)
Construtor temporrio (A temporary constructor)
Klass
Prototypal inherited
Mix-ins
Borrowing Methods
07 Design Patterns
Segue o padro Gof adaptado para javascript, uma vez que ele no possui o conceito de classes.
Singleton
Objetivo obter sempre a mesma instncia de um objeto e poder restringir a quantidade de
instncias de um objeto no programa. Existem muitas formas de fazer isso em javascript, as
melhores no usam globals para evitar sobrescrita ou uso indesejado do objeto.
// forma mais tradicional
function Universe () {
// realiza o cache do primeiro objeto criado
if (typeof Universe.instance == 'object') {
return Universe.instance;
}

this.start = 0;
this.bang = 'big';

Universe.instance = this;

return this;
}

// segunda forma mais parecida com javascript


// usando closures

// Sing apenas um ponteiro para o construtor


let Sing;

(function () {
// o cache acontece numa varivel privada
// evitando uso de globals
let instance = null;

// cria o construtor
// para o ponteiro acima
Sing = function Sing() {
if (instance) {
return instance;
}

instance = this;
this.start = 0;
this.banc = 'Sing';
};

}());

Fbrica
Objetiva ser um centralizador de criao de novos objetos, sejam instncias de uma mesma classe
ou no. O mais importante que, os objetos criados tenham a mesma interface, seja por tipagem
forte ou por convenso. Em linguagens fracamente tipadas, voc pode retornar at colees de
objetos com interfaces diferentes, mas cuidado com isso.
// fbrica na forma clssica
function CarMaker() {}
CarMaker.prototype.drive = function() {
console.log('Vrom, ' + this.doors + ' doors');
};

CarMaker.factory = function (type) {


var constr = type;
var newcar = null;

if (typeof CarMaker[constr] !== 'function') {


throw {
name: 'Error',
message: constr + ' not found',
};
}

if (typeof CarMaker[constr].prototype.drive !== 'function') {


CarMaker[constr].prototype = new CarMaker();
}

newcar = new CarMaker[constr]();


return newcar;
};

CarMaker.Compact = function() {
this.doors = 4;
};

CarMaker.Convertible = function() {
this.doors = 2;
};

CarMaker.SUV = function() {
this.doors = 24;
};

var corolla = CarMaker.factory('Compact');


var solstice = CarMaker.factory('Convertible');
var cherokee = CarMaker.factory('SUV');

corolla.drive();
solstice.drive();
cherokee.drive();

Apesar de ser a forma clssica, achei o cdigo muito complexo.


Iterator
Objetiva fornecer uma interface de navegao por uma coleo de objetos. Alm de ser muito
complexo, no vi muita utilidade para javascript, uma vez que temos arrays que aceitam objetos de
vrios tipos diferentes. De qualquer forma, a vai o padro. Ah! ES6 tambm possui um Iterator,
tornando esse padro obsoleto.
var agg = (function () {
var index = 0,
data = [1, 2, 3, 4, 5],
length = data.length;

return {
next: function () {
var element;
if (!this.hasNext()) {
return null;
}
element = data[index];
index = index + 2;
return element;
},
hasNext: function () {
return index < length;
},
rewind: function() {
index = 0;
},
current: function () {
return data[index];
}
};
}());

Decorator
Como o prprio nome indica, o padro serve para decorar um objeto com outros objetos, onde cada
um possui parte do processamento. No final, executamos todos os objetos usados como decorao
para obter o resultado final. Muito til para lanchonetes e restaurantes.
Novamente achei complicado. No ES6 deve existir uma forma mais simples de implementar isso.
// Implementao da classe principal
function Sale(price) {
this.price = price || 100;
this.decorlist = [];
}

Sale.prototype.decorate = function (decorator) {


this.decorlist.push(decorator);
};

// execuo de todos os decorators


Sale.prototype.getPrice = function () {
var price = this.price;
var i = 0;
var max = this.decorlist.length;
var name = null;

for(i = 0; i < max; i += 1) {


name = this.decorlist[i];
price = Sale.decorators[name].getPrice(price);
}

return price;
};

// Como implementar os decorators com parte do processamento


Sale.decorators = {};
Sale.decorators.fedtax = {
getPrice: function (price) {
return price + (price * 5 / 100)
}
};

Sale.decorators.quebec = {
getPrice: function (price) {
return price + (price * 7.5/100);
}
};

Sale.decorators.money = {
getPrice: function (price) {
return "$" + price.toFixed(2);
}
};

// Como usar o que foi implementado


var sale = new Sale(150);
sale.decorate('fedtax');
sale.decorate('quebec');
sale.decorate('money');
console.log(sale.getPrice());

Strategy
Permite que voc selecione algortmos em tempo de execuo. Basta envolver cada algoritmo como
uma classe ou objeto e depois configuar um objeto principal para depois executar todos os
algoritmos selecionados. A sua implementao um pouco diferente de decorators, pois ele no
acumula algoritmos e o resultado dos algoritmos no interferem no resultado dos outros.
var Validator = {
types: {},
messages: [],
config: {},
validate: function (data) {
var prop = null;
var msg = '';
var type = null;
var checker = null;
var result_ok = false;

// reset todas as mensagens


this.messages = [];

for (prop in data) {


if (data.hasOwnProperty(prop)) {
type = this.config[prop];
checker = this.types[type];

if (!type) {
continue; // nada para vlidar
}

if (!checker) {
// algo ruim aconteceu
throw {
name: 'ValidationError',
message: 'No handler to validate type ' + type
};
}

result_ok = checker.validate(data[prop]);
if (!result_ok) {
msg = 'invalid value for *' + prop + '*, ' + checker.instructions;
this.messages.push(msg);
}
}
}

return this.hasErrors();
},

// helper
hasErrors: function () {
return this.messages.length !== 0;
}
};

// implementao dos tipos


Validator.types.isNoEmpty = {
validate: function (value) {
return value !== '';
},
instructions: 'the value cannot be empty'
};

Validator.types.isNumber = {
validate: function (value) {
return !isNaN(value);
},
instructions: 'the value can only be a valid number, e.g. 1, 3, 3.14 or 2010'
};

Validator.types.isAlphaNum = {
validate: function (value) {
return !/[^a-z0-9]/i.test(value);
},
instructions: 'the value can only contain characters and numbers, no special
symbols'
};

// usando o validador
var data = {
first_name: 'Super',
last_name: 'Man',
age: 'unknown',
username: 'o_O'
};

Validator.config = {
first_name: 'isNoEmpty',
age: 'isNumber',
username: 'isAlphaNum'
};

Validator.validate(data);
if (Validator.hasErrors()) {
console.log(Validator.messages.join("\n"));
}

Faade
Serve de interface para um conjunto de funcionalidades. Geralmente um mtodo que encapsula
vrios outros mtodos. Abaixo um faade que identifica e executa o bloqueio de um evento em
vrios navegadores.
var myevent = {
// ...
stop: function (e) {
// others
if (typeof e.preventDefault === "function") {
e.preventDefault();
}
if (typeof e.stopPropagation === "function") {
e.stopPropagation();
}
// IE
if (typeof e.returnValue === "boolean") {
e.returnValue = false;
}
if (typeof e.cancelBubble === "boolean") {
e.cancelBubble = true;
}
}
// ...
};

Proxy
usado para interceptar o comportamento de um objeto e modificar a forma como ele interage com
outros objetos. Ele pode ser usado como container de outro objeto para vrios fins, como lazy load,
cache de dados, log de execues e etc.
O exemplo clssico bem complexo e no consegui entender, mas o recurso de Proxy do ES6 me
pareceu muito mais simples e fcil de usar.

Mediator
Faz a mediao entre dois ou mais objetos, de forma que os objetos sabem pouco ou nada sobre os
outros. Toda a comunicao realizada pelo mediador.
Imagine um quadro de pontos e vrios jogadores. Cada vez que um jogador joga, ele ganha um
ponto que mostrado no quadro de pontos. Quando o jogador joga, ele avisa ao mediador que
jogou, ento o mediador contabiliza os pontos e atualiza o quadro de pontos. Dessa forma a
manuteno nas classes jogador e quadro de pontos fica mais fcil, criando um acoplamento leve
entre jogadores e quadro.
// classe para jogadores
function Player (name) {
this.points = 0;
this.name = name;
}

Player.prototype.play = function () {
this.points += 1;
mediator.played();
};

// quadro de pontos
var quadro = {
update: function(score) {

console.log('');
for (var prop in score) {
if (score.hasOwnProperty(prop)) {
console.log(prop, ':', score[prop]);
}
}
console.log('');

}
};

var mediator = {
players: {},

setup: function () {
var players = this.players;
players.home = new Player('Home');
players.guest = new Player('Guest');
},

played: function () {
var players = this.players;
var score = {
Home: players.home.points,
Guest: players.guest.points
};

quadro.update(score);
}
};

// jogando
mediator.setup();
mediator.players.home.play();
mediator.players.home.play();
mediator.players.guest.play();
mediator.players.guest.play();
mediator.players.guest.play();

Observer
Novamente, acredito que o autor se complicou aqui. Esse exemplo poderia ser bem mais simples.
Observer consiste num padro onde temos vrios assinantes, um publicador e um observador.
Sempre que um assinate assina o publicador, o publicador armazena o assinante. Sempre que o
publicador tiver uma novidade, ele notifica todos os assinantes armazenados.
timo para criar um baixo acoplamento entre objetos.
var publisher = {
subscribers: {
any: [] // event type
},

subscribe: function (fn, type) {


type = type || 'any';
if (typeof this.subscribers[type] == 'undefined') {
this.subscribers[type] = [];
}

this.subscribers[type].push(fn);
},

unsubscribe: function (fn, type) {


this.visitSubscribers('unsubscribe', fn, type);
},

publish: function (publication, type) {


this.visitSubscribers('publish', publication, type);
},

visitSubscribers: function (action, arg, type) {


var pubtype = type || 'any';
var subscribers = this.subscribers[pubtype];
var max = subscribers.length;

for (var i = 0; i < max; i += 1) {


if (action == 'publish') {
subscribers[i](arg);
}
else {
if (subscrbe[i] == arg) {
subscribers.splice(i, 1);
}
}
}
}
};

function makePublisher(o) {
for (var i in publisher) {
if (publisher.hasOwnProperty(i) && typeof publisher[i] == 'function') {
o[i] = publisher[i];
}
}

o.subscribers = {any: []};


}

var paper = {
dialy: function () {
this.publish('big new today');
},
monthly: function () {
this.publish('interesting analysis', 'monthly');
}
};

var joe = {
drinkCoffe: function (paper) {
console.log('just read', paper);
},
sundayPreNap: function (monthly) {
console.log('About to fall a sleep reading this', monthly);
}
};

makePublisher(paper);
paper.subscribe(joe.drinkCoffe);
paper.subscribe(joe.sundayPreNap, 'monthly');

paper.dialy();
paper.dialy();
paper.dialy();
paper.monthly();
08 Padres para o DOM e Browser
Este capttulo trata dos padres usados para manipulao do DOM e controle de eventos melhorado.
Separao e contedo
1. Contedo (HTML)
2. Apresentao (CSS)
3. Comportamento (JavaScript)
Separar o contedo de acordo com cada funo ajuda a saber onde realizar alteraes no caso de
problemas. Uma alterao na apresentao resulta na mudana de vrios lugares ou pginas do
contedo HTML.
Aprimoramento progressivo
Inicia com a experincia bsica (HTML) para navegadores (user agent) mais simples, adicionando
mais riqueza de experincia para navegadores (user agent) mais capazes. Depois do HTML, se o
navegador suporta CSS, ento o usurio ter uma melhor apresentao para o contedo. Quando o
navegador suporta JavaScript, ento a aplicao/site ganha mais caractersticas de interao com o
usurio.
Para fazer isso:
1. Teste a pgina com suporte a CSS desligado. Verifique se a pgina ainda til e legvel para
o usurio.
2. Teste a pgina com suporte JavaScript desligado. Se o objetivo da pgina ainda
cumprido, todos os links e formulrios funcionam.
3. No use indentificadores inline como onclick e style atributos.
4. Use HTML semntico como tags de cabealho H1...6 e listas.
5. Atualizaes com arquivos menores e mais objetivos.
O uso de javascript deve ser discreto (unobstrusive), no deve estar no caminho do usurio ou
tornar a pgina inutilizvel para navegadores antigos, e no deve ser requisito para a pgina
funcionar. Em vez disso, tente sempre melhorar a pgina de outras formas.
Lembre da deteco de capacidades, no testando o user agent do navegador. Em vez disso,
mais rpido e produtivo testar se existem mtodos ou propriedades no ambiente corrente do
navegador. Nada de user agent sniffing.
// antipattern
if (navigator.userAgent.indexOf('MSIE') !== 1) {
document.attachEvent('onclick', console.log);
}

// better
if (document.attachEvent) {
document.attachEvent('onclick', console.log);
}

// or even more specific


if (typeof document.attachEvent !== "undefined") {
document.attachEvent('onclick', console.log);
}

Acesso e atualizao do DOM


Evite atualizaes do DOM ao mximo. Sempre crie o contedo como string e, por ltimo, atualize
o DOM, evitando o custo de vrias renderizaes no navegador.
1. Evite ao mximo acesso ao DOM.
2. Atribua referncias DOM para variveis locais e trabalhe com essas variveis.
3. Sempre use a API de seletores para consultar elementos do DOM.
4. Faa cache do length quando iterar sobre HTML Collections.
5. Sempre prefira usar atributos id= para facilitar consultas no DOM.
// antipattern loop
for (var i = 0; i < 100; i += 1) {
document.getElementById("result").innerHTML += i + ", ";
}

// better - update a local variable


var i, content = "";
for (i = 0; i < 100; i += 1) {
content += i + ",";
}
document.getElementById("result").innerHTML += content;

// antipattern
var padding = document.getElementById("result").style.padding;
var margin = document.getElementById("result").style.margin;

// better
var style = document.getElementById("result").style;
var padding = style.padding;
var margin = style.margin;

// Using selector APIs means using the methods:


document.querySelector("ul .selected");
document.querySelectorAll("#widget .class");

DOM Manipulation
Use document fragment para evitar renderizaes desnecessrias. Basta criar um fragment, clonar
nodes HTML, realizar modificaes e substituir o n HTML principal pelo fragment. Assim, suas
alteraes ocorrem offline e nenhuma renderizao realizada antes de terminar as alteraes
completamente.
var p, t, frag;
frag = document.createDocumentFragment();

p = document.createElement('p');
t = document.createTextNode('first paragraph');
p.appendChild(t);
frag.appendChild(p);

p = document.createElement('p');
t = document.createTextNode('second paragraph');
p.appendChild(t);
frag.appendChild(p);

document.body.appendChild(frag);

Usando o cloneNode...
var oldnode = document.getElementById('result'),
clone = oldnode.cloneNode(true);
// work with the clone...
// when you're done:
oldnode.parentNode.replaceChild(clone, oldnode);

Events e Handle Events


Por compatibilidade de navegadores, sempre que recuperar um evento do DOM use o padro:
function myHandler(e) {
var src, parts;
// get event and source element

e = e || window.event;
src = e.target || e.srcElement;

// actual work: update label


parts = src.innerHTML.split(": ");
parts[1] = parseInt(parts[1], 10) + 1;
src.innerHTML = parts[0] + ": " + parts[1];

// no bubble
if (typeof e.stopPropagation === "function") {
e.stopPropagation();
}
if (typeof e.cancelBubble !== "undefined") {
e.cancelBubble = true;
}

// prevent default action


if (typeof e.preventDefault === "function") {
e.preventDefault();
}

if (typeof e.returnValue !== "undefined") {


e.returnValue = false;
}
}

Event Delegation
Consiste em filtrar os eventos emitidos por ns no seu container, no atribuindo o onclick, por
exemplo, nos ns filhos. Assim, voc pode adicionar e remover ns filhos sem se preocupar em
anexar um listener em cada n filho.
Exige um pouco mais de trabalho, memria e processamento do navegador. Mas o cdigo limpo,
velocidade de desenvolvimento e manutenabilidade compensam o custo dessa implementao.
<div id="click-wrap">
<button>Click me: 0</button>
<button>Click me too: 0</button>
<button>Click me three: 0</button>
</div>

No onclick da div click-wrap voc ter:


// ...
// get event and source element
e = e || window.event;
src = e.target || e.srcElement;
if (src.nodeName.toLowerCase() !== "button") {
// executa o codigo no click do boto
return;
}
// ...

Scripts de longa durao


Padro pensado naqueles cdigos que executam durante muito tempo. Quando isso acontece, o
navegador mostra uma mensagem para o usurio cancelar a execuo do script e trava toda a pgina
enquanto o usurio no responder.
Para resolver o problema, o usurio pode quebrar a execuo do script em pequenos
processamentos e simular trheads com a mtodo setTimeout. Em navegadores mais modernos,
pode-se usar Web Workers.
Web Workers
Consiste em usar um script, dentro de um arquivo JS como processo dentro de um objeto
WebWorker. No arquivo JS voc vai ter:
var end = 1e8, tmp = 1;

// postMessage emit o evento onmessage no webWorker


postMessage('hello there');

while (end) {
end -= 1;
tmp += end;
if (end === 5e7) { // 5e7 is the half of 1e8
postMessage('halfway there, `tmp` is now ' + tmp);
}
}

postMessage('all done');

No main.js voc tem o WebWorker usando o arquivo js acima.


var ww = new Worker('my_web_worker.js');
ww.onmessage = function (event) {
document.body.innerHTML +=
"<p>message from the background thread: " + event.data + "</p>";
};

// o resultado impresso
message from the background thread: hello there
message from the background thread: halfway there, `tmp` is now 3749999975000001
message from the background thread: all done

Remote Scripting
Consiste em usar Ajax (XMLHttpRequest) ou JSONP para se comunicar com o servidor. A parte de
ajax j conheo bem por causa do JQuery e VueResource.
No entendi como funciona o JSONP, procure mais informaes sobre o assunto.
Voc tambm pode usar Frames ou Image Beacons. Isso significa apontar o src de uma imagem
direto para um script no servidor.
new Image().src = "http://example.org/some/page.php";
Assim, quando a imagem adicionada no DOM, o servidor gera uma imagem e ela fica visvel na
pgina. Voc tambm pode usar a tcnica do iframes para mudar o contedo de uma pgina sem
precisar recarreg-la completamente.
Deploying JavaScript
O autor indica a leitura de dois livros para aprofundar o assunto. High Performance Web Sites e
Even Faster Web Sites da OReilly.
Consiste em saber como organizar seus scripts para mais alta performance em ambiente de
produo. As tcnicas so Combinin Scripts, Minifying and Compressing, Expires Header e Using
CDN.
Combining Scripts
A ideia juntar vrios pequenos scripts num arquivo maior. Um navegador carrega apenas 3 scripts
ao mesmo tempo. Por isso, mais rpido carregar menos arquivos ao invs de carregar vrios
arquivos pequenos.
1. muito fcil combinar arquivos usando o comando cat do linux.
cat jquery.js jquery.quickselect.js jquery.limit.js > all.js
2. Voc pode unificar arquivos que dificilmente sofrem alteraes, para usar o cache do
navegador. Assim, quando alterar os outros arquivos, o navegado baixar apenas os arquivos
alterados, diminuindo o tempo de download.
3. Voc tambm pode versionar seus arquivos unificados, como all_20102017.js ou usar u hash
do contedo. Apesar de ser um pouco mais trabalhoso, isso quebra o cache forando o
navegador a baixar sempre os arquivos alterados e continuar usando o cache dos que no
sofreram alteraes.
S lembrando que isso para ambiente de produo. Em ambiente de desenvolvimento, no
necessrio por causa do Debuging.
Minifying and Compressing
Alm que minificar e comprimir (uglify) seus arquivos de script, voc pode utilizar o arquivo
.htaccess para informar ao apache que os arquivos devem ser compactados antes de serem enviados
ao navegador do usurio. Reduzindo o trfego de dados na rede.
AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml
application/javascript application/json
Pesquise como fazer isso no nginx..
Expires Header
Tambm usa o apache para determinar uma data de expirao para os arquivos de script. Assim,
pode manter alguns arquivos em cache e outros com previso de alterao no Expires Header do
arquivo .htaccess.
ExpiresActive On
ExpiresByType application/x-javascript "access plus 10 years"

Isso interessante apenas para arquivos que no possuiem versionamento. Caso seu arquivo tenha
um nmero de verso ou hash do contedo, o cache ser gerenciado pelo versionamento.
Pesquise como fazer isso no nginx..
Using CDN
Tambm serve para melhorar a velocidade de carregamento das pginas. O limite de 3 downloads
de scripts ao mesmo tempo realizado por domnio. Sendo assim, se voc tiver scripts hospedados
em outros domnios, o download ser realizado numa maior quantidade simultnea.
O Google possui um CDN para scripts pupulares como jquery.

Microsoft possuir CDN para jquery e suas prprias bibliotecas.

Estratgias de carregamento
O melhor lugar para o <script>, antes do final da tag <body>. Assim, garantimos que o script ser
executado depois que o contedo do DOM for carregado. Tambm exibimos a pgina esttica mais
rapidamente, sem a necessidade de executar alguma javascript.
<!doctype html>
<html>
<head>
<title>My App</title>
</head>
<body>
...
<script src="all_20100426.js"></script>
</body>
</html>

HTTP Chinking
No entendi isso procure mais informaes sobre o assunto.
Dynamic <script> Element
Consiste em carregar scripts dinamicamente, criando um elemento <script> no navegador para
executar comandos vindos do servidor. O padro mais usado ter uma lista de funes armazenados
num array e depois executar cada funo ao fim da pgina. As funes, como esperado, so
carregados de scripts baixados dinmicamente do servidor.
var mynamespace = {
inline_scripts: []
};

// vindo do servidor
<script>
mynamespace.inline_scripts.push(function () {
console.log("I am inline");
});
</script>

// executado ao fim da pgina


var i, scripts = mynamespace.inline_scripts, max = scripts.length;
for (i = 0; i < max; max += 1) {
scripts[i]();
}

Esses scripts podem ser anexados ao Header da pgina via DOM. Criando a tag <script
src=script_servidor.js/> usando appendChild para adicionar o script no Header.
Lazy-Loading
Parecido com o anterior, mas carregando os scripts apenas quando necessrio para sua execuo.

<script>
window.onload = function () {
var script = document.createElement("script");
script.src = "all_lazy_20100426.js";
document.documentElement.firstChild.appendChild(script);
};
</script>
</body>
</html>

Loading on Demand
Consiste em criar uma funo chamada require, que executa um Ajax para baixar o contedo do
javascript. Depois de baixado, criamos a tag <script> adicionamos ela na pgina. Assim voc pode
baixar apenas os arquivos que precisa, quando precisar deles.
function require(file, callback) {
var script = document.getElementsByTagName('script')[0],
newjs = document.createElement('script');

// IE
newjs.onreadystatechange = function () {
if (newjs.readyState === 'loaded' || newjs.readyState === 'complete') {
newjs.onreadystatechange = null;
callback();
}
};

// others
newjs.onload = function () {
callback();
};

newjs.src = file;
script.parentNode.insertBefore(newjs, script);
}
// script no servidor
// ondemand.js.php
<?php
header('Content-Type: application/javascript');
sleep(1);
?>
function extraFunction(logthis) {
console.log('loaded and executed');
console.log(logthis);
}

// main.js
// execuo quando necessrio
require('ondemand.js.php', function () {
extraFunction('loaded from the parent page');
document.body.appendChild(document.createTextNode('done!'));
});

Preloading Script
Consiste em carregar os scripts em objetos de Imagem e Object Element. Assim, voc carrega
previamente seus scripts para depois adicionlos na pgina. A diferena do Preloading para On
Demand e lazy-Loading o momento de baixar os sccripts. Preloading um download pevio
enquanto os outros dois so downloads posteriores ao carregamento da pgina.
// carregando como imagem
new Image().src = "preloadme.js";

// carregando como object


var obj = document.createElement('object');
obj.data = "preloadme.js";
document.body.appendChild(obj);

// mtodo preloading
var preload;
if (/*@cc_on!@*/false) {
// IE sniffing with conditional comments
preload = function (file) {
new Image().src = file;
};
} else {
preload = function (file) {
var obj = document.createElement('object'),
body = document.body;

obj.width = 0;
obj.height = 0;
obj.data = file;
body.appendChild(obj);
}
};

// usando a funo preloading


preload('my_web_worker.js');

Potrebbero piacerti anche