javascript avançado
DESCRIPTION
Javacript Avançado: herança, closures, curry, bind, partes feias, partes ruinsTRANSCRIPT
JAVASCRIPT AVANÇADORicardo Cavalcanti
Algumas Ferramentas
“Firebug integrates with Firefox to put a wealth of web development tools at your fingertips while
you browse. You can edit, debug, and monitor CSS, HTML, and JavaScript live in any web page!”
http://getfirebug.com
Prototype: introdução
Não é a biblioteca prototype.js Javascript usa protótipos no seu modelo
de objetos O que importa é o que o objeto faz, e não
de quem ele herda Uma função construtora seta seu
prototype no objetos que cria
Prototype: a propriedade prototype
Todo objeto tem uma propriedade prototype
Ao tenta acessar uma propriedade inexistente, javascript busca a propriedade no prototype do objeto E no prototype do prototype...
Prototype: aumentando prototypes
Ao incluir propriedades e funções ao prototype de um construtor, elas são incluidas nos objetos que o construtor produzvar cat = new Cat("Barsik");
Cat.prototype.animal = "cat";Cat.prototype >> {animal: "cat"}cat.animal >> "cat"Cat.prototype.seeADog = function() {
return "I'm "+this.name+". Sshhhhhhh!";}
cat.seeADog() >> "I'm Barsik. Sshhhhhhh!";
Prototype: mudando o prototype
Mudanças no prototype afetam todas as instâncias
var cat = new Cat("Barsik");var cat2 = new Cat("Murzik");
Cat.prototype.animal = "cat";cat.animal >> "cat"cat2.animal >> "cat“
Cat.prototype.animal = "dog";cat.animal >> "dog"cat2.animal >> "dog"
Prototype: propriedades
O objeto pode ter suas próprias propriedades A delegação cuida da prioridadefunction Gadget(/*String*/ name) {
this.name = name;}var iphone = new Gadget("iPhone");Gadget.prototype.name = "none";
iphone.name >> "iPhone"delete iphone.name;iphone.name >> "none"
Prototype: estendendo JS Objects
Podemos estender inclusive os JS Objects
String.prototype.trim = function(){return this.replace(/^\s+|\s+$/g, '');
};
“ bla “.trim() >> “bla”
HERANÇA
Herança clássica
Objetos são instancias de classes
Classes herdam de outra classe
Herança prototípica
Não há classes Objetos herdam de objetos Um objeto tem um link para outro objeto No Firefox é __proto__
var newObject = Object(oldObcet)
newObject
__proto__
oldObject
Herança prototípica
var oldObject = { firstMethod: function(){...}, secondMethod: function(){...},};
var newObject = Object(oldObject);
newObject.thirdMethod = function(){...};
var anotherObject = Object(newObject);
anotherObject.thirdMethod();
Herança prototípica
Ao acessar uma propriedade, o interpretador vai primeiro no objeto,
depois busca no protótipo do objeto, ...depois no protótipo do protótipo... Até Object.prototype
foo 2 foo 1
newObject oldObject
Herança prototípica
Mudanças no oldObject serão visíveis no newObject, como já vimos
Mudanças no newObject não alteram o oldObject
foo 2 foo 1
newObject oldObject
Herança pseudoclássica
Com herança prototípica pura, a linguagem deve ter um operador como a função Object Para criar novos objetos utilizando um já
existente
Javascript usa operadores aparentemente clássicos Mas que funcionam prototipicamente
Pseudoclássica
Três mecanismos: Funções Contruturas O operador new O atributo prototype, nas funções
Operador new
function Contructor(){ this.member = ...;}
Contructor.prototype.firstMethod = function(a,b) {...}
Contructor.prototype.secondMethod = function(a,b) {...}
var newObject = new Constructor();
Operador new
new Constructor() retorno um novo objeto ligado ao Constructor.prototype
newObject Constructor.prototype
newObject oldObject
Operador new
A função Contructor() pasa o novo objeto na variável this Para inicizar o novo objeto
newObject Constructor.prototype
prototype
Cada objeto function criado tem um atributo prototype
O prototype tem um atributo constructor, que refencia o objeto function
Outros atributos adicionados no prototype da função serão vistos nos objetos constuídos com ela
É possível criar constantes e métodos par aos objetos
Herança Pseudoclássica
É possível simular herança clássica atribuindo um objeto criado por um constutor como o protótipo de outra
Mas não é exatamente herança clássica
function BiggerConstructor(){};BiggerConstructor.prototype = new
MyContructor()
function SuperClasse(){}
SuperClasse.prototype.hello = function(){alert('super hello')};
function SubClasse(){};SubClasse.prototype = new SuperClasse();SubClasse.prototype.subhello = function(){alert('sub hello')};
var o = new SubClasse();
o.hello();o.subhello();
function SuperClasse(){}
SuperClasse.prototype.name = 'super';
SuperClasse.prototype.hello = function(){alert(this.name)};
function SubClasse(){
this.name = 'sub';
};
SubClasse.prototype = new SuperClasse();
SubClasse.prototype.subhello = function(){alert(this.name)};
var o = new SubClasse();
o.hello();
o.subhello();
CLOSURES
Escopo nas funções
O escopo é definido pelas funções, e não pelos blocos
Funções internas têm acesso às variáveis das funções externas Exceto this e argumentsfunction f (){ var y = 3; var g = function(){ var x = 2+ y; ... }}
E se a função interna durar mais que a função externa?
Closures
Quando a função interna dura mais que a externa, o escopo externo é preservado Isso se chama closure
Um closure simples
var f = function(x){ var m = function(y){ return x * y; } return m;}var dobro = f(2);var triplo = f(3);
alert(dobro(10));alert(triplo(10));
Quando f() é executado, um novo m() é criado
Enquanto m() existir, seu escopo existe, e o do seu ‘pai’ também
Usando closures
Information Hiding Funções com timers Event handlers Callbacks
Clojures: Dados privados
var quo = function (status) { return { get_status: function ( ) { return status; } };};
var myQuo = quo("amazed");alert(myQuo.get_status( ));
Closures: timers
var fade = function (node) {
var level = 1;
var step = function ( ) {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
if (level < 15) {
level += 1;
setTimeout(step, 100);
}
};
setTimeout(step, 100);
};
fade(document.body);
Closure: event handler
Função que adiciona um event handler para uma lista de nodes. Ao clicar, alerta a ordem do nó.
var add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (e) { alert(i); } }}; Qual o resultado?
Closure: event handler
Exemplo corretovar add_the_handlers = function (nodes) { var i; for (i = 0; i < nodes.length; i += 1) { nodes[i].onclick = function (i) { return function (e) { alert(i); }; } (i); }};
CURRY
Curry
Funções são valores É possível combinar uma função com
alguns argumentos e produzir outra funçãovar mult = function (a, b) {return a * b};var duplicar = mult.curry(2);var duplicar = function (b){ retunr 2*b};
Função curry
Curry cria um closure que guarda a função original e seus argumentos para realizar o currying
Quando executada, ela retorna o resultado da função original com os argumentos passados ao curry e os passados a ela
var mult = function (a, b) {return a * b};var duplicar = mult.curry(2);var seis = duplicar(3); //mult.curry(2)(3) >>> 6
Function.prototype.curry = function ( ) { var args = arguments, that = this; return function ( ) { return that.apply(null,
args.concat(arguments)); };}); //PROBLEMA: arguments não é um
array
Função curry: final
Function.prototype.curry = function ( ) { var slice = Array.prototype.slice, args = slice.apply(arguments), that = this; return function ( ) { return that.apply(null,
args.concat(slice.apply(arguments))); };});
Closures: callbacks
var replace = function (el, url){
var callback = function(t){ el.innerHTML = t; }; makeXHR(callback, url); // ajax}
replace (myEl, minhaURL);
Closures : cuidados
Evite criar funções em loops Para evitar closures indesejados
Atenção com o this Mais sobre isso com bind
BIND
This
window.name = "the window object"
function scopeTest() { return this.name}
scopeTest()// -> "the window
object"
var foo = { name: "the foo
object!", otherScopeTest:
function() { return this.name }
}
foo.otherScopeTest()// -> "the foo object!"
Javascript é dinâmica
//atribuindo a função, SEM executá-lawindow.test = foo.otherScopeTest
//chamando a funçãotest() ; // -> "the window object"
Function bind
Eu gostaria de escolher o ‘this’ da minha funçãowindow.test = foo.otherScopeTest.bind(foo);test() // -> "the foo object!"
Function.prototype.bind = function(scope) { var _function = this; return function() { return _function.apply(scope, arguments); }}
PARTES FEIASRicardo Cavalcanti
Variáveis globais
Todas as coisas são carregadas no mesmo objeto global
var foo = value;window.foo = value;foo=value;
Escopo
Apesar da sintaxe de C Blocos não criam escopo O escopo é sempre da função
Inserção de ;
Há um mecanismo de completar com ; alguns comandos
return { status: true };
return { status: true };
return ;{ status: true };
=
Palavras reservadas
abstract boolean break byte case catch char class const continue debugger default delete do double else enum export extends false final finally float for function goto if implements import in instanceof int interface long native new null package private protected public return short static super switch synchronized this throw throws transient true try typeof var volatile void while with
var method; // okvar class; // illegalobject = {box: value}; // okobject = {case: value}; // illegalobject = {'case': value}; // okobject.box = value; // okobject.case = value; // illegalobject['case'] = value; // ok
Unicode
char em Javascript tem 16bits
Um char unicode são 2 caracteres em javascript
typeof
typeof 98.6 >> ‘number’
typeof null >> ‘object’
my_value === null //melhorif (my_value && typeof my_value === 'object') { // my_value is an object or an array! }
typeof /a/ >> ‘object’ ou ‘function’ depende da implementação
parseInt
Converte um string num integer Para quando vê um não-dígito
parseInt("16") >> 16parseInt("16 mil") >> 16
Se o string começar com 0 é utilizada a base 8parseInt("08") >> 0parseInt("09") >> 0
Parse int pode receber a baseparseInt("08", 10) >> 8
Ponto fllutuante
Javascript usa IEEE Standard for Binary Floating-Point Arithmetic (IEEE 754)
Portanto0.1 + 0.2 !== 0.3
Atenção quando utilizar valores monetários Melhor multiplicar por 100
NaN
Se algum operando for NaN, o resultado da operação será NaN Converter um string em inteiro gera NaN
Porém NaN === NaN // false
Use a função isNaN isNaN(NaN) // true
null e undefined
value = myObject[name]; if (value == null) { alert(name + ' not found.'); } Um erro anula o outro! value é undefined, mas o operador ==
faz o type coersion Cuidado: NaN e undefined são variáveis
globais!
PARTES RUINS
==
'' == '0' // false0 == '' // true0 == '0' // true
false == 'false' // falsefalse == '0' // true
false == undefined // falsefalse == null // falsenull == undefined // true
' \t\r\n ' == 0 // true
with
Para acessar os atributos de um objeto Problemas:
with (obj) { a = b; }
O mesmo queif (obj.a === undefined) { a = obj.b === undefined ? b : obj.b;} else { obj.a = obj.b === undefined ? b : obj.b;}
eval
Evite! Bem como outras formas que recebem
código como string Contrutor de Fuction setTimeout setInterval
Problema de segurança Difícil de checar e de ler
switch Fall Through
Switch-case sem break Fonte de muitos erros, cuidado!
Comandos sem bloco
Sempre use blocos nos comandos
if (ok) t = true;
if (ok) t = true; advance( );
if (ok) { t = true; advance( );}
if (ok) { t = true;}advance( );
vira
parece
Mas naverdade é
Operadores de bits
Os mesmos de java& and| or^ xor~ not>> signed right shift>>> unsigned right shift<< left shift
Convertem para integer e desconvertem Longe do hardware, lentos
Typed Wrappers
Desnecessáriosnew Boolean(false)
Evite o uso de new Boolean, new Number, new String
Também evite new Object e new Array Use {} e []
Para evitar erros