O mínimo que você precisa saber de JavaScript até 3 de Outubro de 2017
Eduardo Aleixo
Quem quiser, pode acompanhar o código em https://gist.github.com/eh-am
- FATEC
- Mendigando emprego em agências
- CSF
- Remoto!
- Devops/Frontend
- Aprenda inglês
- Não tenha vergonha de não saber algo
- Mas também não fique acomodado com isso
- Para aqueles que mexem com JavaScript
- Rápida
- Não se preocupe caso não entenda tudo!
- "Saber o que não sei"
- JavaScript não tem nada a ver com Java (já teve)
"Netscape and Sun did a license agreement and it became JavaScript. And the idea was to make it a complementary scripting language to go with Java, with the compiled language." Brendan Eich
- Mil a seu lado, dez mil à sua direita... (Visual Basic, ActionScript, Dart)
- Mais ou menos Inútil até a invenção do AJAX
- ES5? ES6? ES7?
7 Tipos de representação de dados
var objeto = {}; /* Object */
var numero = 10; /* Number */
var float = 3.33; /* Também é número */
var string = "String" /* String */
var bool = true; /* Boolean */
var nulo = null /* Null */
var semDefinicao = undefined; /* Undefined */
undefined e null?
var indefinido;
console.log('var indefinido = ', indefinido);
var array = [];
console.log('var array[1] = ', array[1]);
function retornaNada() {}
console.log('function retornaNada =', retornaNada());
var a = 'a';
/* ES6 */
let it = 'be';
const Constantino = "O fundador de Constantinopla";
Regra geral: use const
, a não ser que DO FUNDO DA SUA ALMA precise reatribuir.
Porém, tome cuidado:
const object = {
value: 10
};
object.value = 11;
console.log(object);
const array = [1, 2];
array.push(3);
console.log('array =', array);
console.log('typeof array =', typeof array);
Ou seja, somente a referência é constante.
primeiro nem é:
a = 'b'
coerção (implícita) nos preocupa:
console.log('10 == 10', 10 == 10) // true, correto
console.log('"10" == 10', "10" == 10) // true, até que faz sentido
console.log('"" == 0', "" == 0) // true, quê?!
trios pra Pasárgada
console.log('10 === 10', 10 === 10) // true
console.log("'10' === 10", '10' === 10) // false, como previamos
console.log('"" == 0', "" === 0) // false!
JavaScript tem ASI (Automatic Semicolon Insertion) que funciona bem na maioria dos casos:
var a = 0
var b = 1
se torna
var a = 0;
var b = 1;
var foo = function() {
var bar = 'baz'
return
{
bar: bar
}
}
var foo = function() {
var bar = 'baz'
return; // ; indesejada
{
bar: bar
}
}
Resumindo, a não ser que você queira saber como o parser funciona, coloque ponto e vírgula.
Na maior parte das linguagens, temos escopo por bloco:
int x = true;
if (x == true) {
int y = 10;
}
y // não existe aqui fora
Em JavaScript, porém, se usa escopo por função:
function functionScope() {
var x = 0;
if (x === 0) {
var y = 1;
}
console.log(y);
}
functionScope();
Tudo definido no escopo global, fica acessível em qualquer lugar!
var global = 'global';
function facaAlgumaCoisa() {
if (global === 'global') {
return 'Tenho acesso a global';
}
return 'Não tenho acesso a global';
}
facaAlgumaCoisa();
- Exemplos: jQuery, lodash etc
- Contras: name clashing
Por isso, surge o padrão das Self-invoking Functions
(function(){
var notGlobal = "Não sou global";
})();
notGlobal;
Funções internas têm acesso ao conteúdo do pai:
function init() {
var name = 'FATEC'; //by init
function displayName() {
return name; // acesso ao pai
}
return displayName();
}
init();
Pilha de execução
Surgem as chamadas "Closures"
function init() {
var name = 'FATEC';
function displayName() {
return name;
}
return displayName;
}
var displayName = init();
displayName();
Seu uso? Esconder implementação interna e só expor uma API:
function makeBusFareCalculator() {
var fares = {
SOROCABA: 3.80,
VOTORANTIM: 3.95
};
var totalPassages = 0;
return {
calc: function (city) {
totalPassages++;
return fares[city];
},
totalCalculated: function () {
return totalPassages;
}
}
}
var busFareCalculator = makeBusFareCalculator();
busFareCalculator.calc('VOTORANTIM');
busFareCalculator.totalCalculated(); // 1
Um erro comum
function createTimers(total) {
for (var i = 0; i < total; i++ ){
setTimeout(function () {
console.log('Eu sou loop', i);
}, 1000)
}
}
createTimers(5);
Antigamente, uma das soluções era com Self Invoking Functions:
function createTimers(total) {
for (var i = 0; i < total; i++ ){
(function (i) {
setTimeout(function () {
console.log('Eu sou loop', i);
}, 1000)
})(i);
}
}
createTimers(5);
Hoje em dia, é facilmente resolvido com let
function createTimers(total) {
for (let i = 0; i < total; i++ ){
setTimeout(function () {
console.log('Eu sou loop', i);
}, 1000)
}
}
createTimers(5);
Por quê? Porque let (e const) tem escopo em bloco:
const a = 0;
{
const b = 0;
}
console.log('a =', a);
console.log('b =', b);
var a = 10;
Em realidade, é:
var a;
a = 10;
Tá, mas e daí?
var n = 1;
function hoistExemplo() {
console.log('n é', n);
var n = 2;
console.log('n é', n);
}
hoistExemplo();
Um exemplo mais interessante:
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
console.log(foo);
}
bar();
Dicas:
- let/const ao invés de
- 'use strict'
'use strict';
var foo = 1;
function bar() {
if (!foo) {
var foo = 10;
}
console.log(foo);
}
bar();
- só para ser backwards compatible
- pode ser colocado no começo de um arquivo, ou no começo de uma função:
'use strict'
var a;
/* Seu código aqui */
function estrito() {
'use strict'
/* Seu código aqui*/
}
Nos ajuda em diversos casos:
function f(x) {
'use strict';
var a = 12;
b = a + x * 35; // error!
}
f(42);
'use strict'
function argumentosErrados(a, b, a) {
}
argumentosErrados();
Dentre outros
Arrow functions (ES6)
Só syntax sugar para função
function antiga(a, b) {
return a + b;
}
const nova = (a, b) => {
return a + b;
}
//Pode ser escrito de uma forma mais curta
const curta = (a, b) => a + b;
Valores padrão
function addMargin(margin) {
var m = margin || 20;
// blabla
}
// Com ES6
function addMargin(margin = 20) {
// blabla
}
é um padrão comum javascript funções javascript são de primeira ordem (basicamente, podem receber outras funções)
function digaOi() {
console.log('oi');
}
setTimeout(digaOi, 1000);
Faça sua própria função com callback:
function digaOi(callback) {
console.log('oi');
if (typeof callback === "function") callback();
}
digaOi(function () {
console.log('tchau');
});
Callback hell
getPost(id, function(post) {
getUser(post.user, function (user) {
getAllPeopleWithSameTelephone(user.telefone, function (people) {
console.log('Essas sao todas as pessoas com o mesmo telefone', people);
})
})
})
No more Callback Hell
getPost(id)
.then(post => getUser(post.user))
.then(user => getAllPeopleWithSameTelephone(user.telefone))
.then()
Promise é uma promessa de retorno
function minhaPromise(valor) {
const promise = new Promise((resolve, reject) => {
// busque alguma coisa no banco,
// faca algum calculo
var result = 1 + valor;
if (result > 0) resolve(result);
else reject(result)
});
return promise;
}
minhaPromise(1)
.then(valor => console.log('valor é maior que 0', valor))
.catch(error => console.log('Caí no catch! valor é menor que 0', error));
minhaPromise(-10)
.then(valor => console.log('valor é maior que 0', valor))
.catch(error => console.log('Caí no catch! valor é menor que 0', error));
Promise.all([promise1, promise2])
Promise.resolve();
minhaPromise()
.then(outraPromise)
then({
// algum codigo síncrono
Promise.resolve(1);
})
.then(maisOutraPromise);
- Native Promises
- BlueBird
- $q
Posso usar tal feature?
http://caniuse.com/#search=await http://node.green/#async-functions
Async Await
AFAIK, na maior parte dos codebases ainda não é muito usado
const makeRequest = async () => {
const value1 = await promise1()
const value2 = await promise2(value1)
return promise3(value1, value2)
}
se refere a quem o chama
no escopo global, é a janela
function quemSouEu() {
return this;
}
// Não podemos imprimir a Window
// porque é uma referência circular
console.log(quemSouEu().constructor.name)
Se for uma propriedade de um objeto (método), é o próprio objeto
var objeto = {
quemSouEu: function () {
return this;
}
}
console.log(objeto.quemSouEu() === objeto)
Apply e Call mudam o 'this'
function populate(field, content) {
this.content = content[field];
}
function getPost(id, post) {
return fetch('https://jsonplaceholder.typicode.com/posts/' + id)
.then(function (res) {
return res.json();
})
.then(function (res) {
populate.call(post, 'body', res);
console.log('post', post);
});
}
var post = { title: 'Post 1' };
getPost(1, post);
Mais sucinto
function getPost(id, post) {
return fetch('https://jsonplaceholder.typicode.com/posts/' + id)
.then(res => res.json())
.then(populate.bind(post, 'body'))
.then(post => console.log(post));
}
function populate(field, content) {
this.content = content[field];
return Promise.resolve(this);
}
var post = { title: 'Post 1' };
getPost(1, post);
Na minha experiência, apply/call só são usado nos seguintes casos:
Usar métodos de array em strings
[].filter.call("joão carlos3234", val => Number.isInteger(parseInt(val)));
E funções com n argumentos
function somaTodos() {
var total = 0;
for (var i = 0; i < arguments.length; i++) {
total += arguments[i]
}
return total;
}
console.log(somaTodos(1, 2, 3, 4, 5));
console.log(somaTodos.apply(null, [1, 2, 3, 4, 5]));
console.log(somaTodos.apply(null, [1, 2 ]));
(Sidenote) arguments
Toda função tem uma propriedade arguments
, que é uma array-like,
um jeito de pegá-los é usando slide
function vejaMeusArguments() {
var args = [].slice.call(arguments, 0); // ES5
const argsArrayFrom = Array.from(arguments); // ES6
console.log('args com slice', args);
console.log('Args com Array From', argsArrayFrom);
}
vejaMeusArguments(1, 2, 3, 4);
A minha preferida mesmo é bind:
runButton.addEventListener('onclick', evaluateCode.bind(null, 'runButton'));
listener.simple_combo("cmd enter", evaluateCode.bind(null, 'keyboard');
function evaluateCode(mode) {
if (mode === 'runButton') {
// faca de um jeito
} else if (mode === 'keyboard') {
// faca de outro
}
}
Resumindo,
Apply e Call chamam imediatamente
Call recebe como diferentes argumentos Ex:
funcao.call(novoThis, argumento1, argumento2, argumento3)
Apply recebe um array de argumentos
funcao.apply(novoThis, [argumento1, argumento2, argumento3])
Bind só "cola" o novoThis, mas não o chama. útil para funções não assíncronas.
Map -> mapeia (duh)
const names = ['JOHN', 'Mary', 'chris', 'paul'];
const uppercasedNames = names.map(name => name.toUpperCase());
console.log('uppercasedNames', uppercasedNames);
ou
function toUpperCase(string) {
// podemos fazer algumas validações aqui
// como verificar se é realmente uma string
return string.toUpperCase();
}
const names = ['JOHN', 'Mary', 'chris', 'paul'];
const uppercasedNames = names.map(toUpperCase);
console.log('uppercasedNames', uppercasedNames);
Filter, filtra (duh)
function isUpperCase(string) {
return string.toUpperCase() === string;
}
const names = ['JOHN', 'Mary', 'chris', 'paul'];
const onlyUpperCasedNames = names.filter(isUpperCase)
console.log('only uppercased', onlyUpperCasedNames);
Reduce, reduz (duh)
const names = ['JOHN', 'Mary', 'chris', 'paul'];
const totalLength = names.reduce(function (acc, next){
return acc + next.length;
}, 0);
console.log('totalLength', totalLength);
Podemos combiná-los
function isUpperCase(string) {
return string.toUpperCase() === string;
}
const names = ['JOHN', 'Mary', 'chris', 'paul'];
const onlyUpperCasedNames = names.filter(isUpperCase)
const totalLength = onlyUpperCasedNames.reduce(function (acc, next){
return acc + next.length;
}, 0);
// total length de nomes maiúsculos
console.log('totalLength', totalLength);
Void 0
Apenas um jeito fresco de escrever undefined
com menos caracteres
console.log('undefined === undefined', undefined === undefined);
console.log('void 0 === void 0', void 0 === void 0);
console.log('undefined === void 0', void 0 === undefined);
Concatenação de String
Há várias maneiras
var catalonia = "Catalonia no es";
var espana = "España"
console.log('catalonia + espana', catalonia + espana);
console.log('catalonia.concat(espana)', catalonia.concat(espana));
console.log(`${catalonia} ${espana}`, `${catalonia} ${espana}`); // ES6
// Porém
console.log("Catalonia no es" + null);
console.log("Catalonia no es".concat(undefined));
//Então
console.log([catalonia, " ", null, undefined, espana].join(""));
// Ou, usando o conhecimento anterior
console.log([catalonia, null, undefined, false, "", void 0, espana].filter(s => s && s.length).join(" "));
Ao invés de
function funcao(valor) {
if (valor > 0) {
// faca isso
} else {
// n faca nada
}
}
function funcao(valor) {
if (valor < 0) return;
// codigo normal
}
!! = converter para boolean
const user = {
firstName: 'John',
lastName: ''
}
console.log('tem sobrenome', !!user.lastName);
if (user) {
setName(user);
}
if (user) setName(user);
user ? setName(user) : '';
user && setName(user);
Verificando se um objeto tem determinada propriedade
const joao = {
firstName: 'joao',
lastName: 'martins'
}
if ('lastName' in joao) {
console.log('Joao tem lastName');
}
if('age' in joao) {
console.log('Joao tem age');
}
(rodar no console)
if (!("a" in window)) {
var a = 1;
}
alert(a);
function a(x) {
return x * 2;
}
var a;
alert(a);
function b(x, y, a) {
arguments[2] = 10;
console.log(a);
}
b(1, 2, 3);
(rodar no console)
function a() {
alert(this);
}
a.call(null);
If thisArg is null or undefined, the called function is passed the global object as the this value.
function Person(name) {
this.name = name;
}
var person = Person('John');
console.log(person);
Só para não acharem que estou mentindo, aqui está um código que escrevi hoje:
const pages = {
PAGE_1: { mobile: [{ state: 'y' }], desktop: [{ state: 'x' }]},
PAGE_2: { mobile: [{ state: 'z' }] },
PAGE_3: { desktop: [{ state: 'x' }]},
}
Object.keys(pages).reduce((acc, key) => {
const mobileStates = pages[key].mobile && pages[key].mobile.map(val => val.state);
const desktopStates = pages[key].desktop && pages[key].desktop.map(val => val.state);
return acc.concat(mobileStates || []).concat(desktopStates || [])
}, []);
Obrigado!
eduardoaleixo.com
twitter: @maneatingbear (games, cultura espanhola/japonesa, HQs)
-
https://github.com/mbeaudru/modern-js-cheatsheet/blob/master/readme.md
-
https://toddmotto.com/everything-you-wanted-to-know-about-javascript-scope/
-
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
semicolons
- http://inimino.org/~inimino/blog/javascript_semicolons
- http://blog.izs.me/post/2353458699/an-open-letter-to-javascript-leaders-regarding
hoisting
- http://www.adequatelygood.com/JavaScript-Scoping-and-Hoisting.html
- http://ignaciothayer.com/post/a-dangerous-example-of-javascript-hoisting/
quiz (OLHAR)
- https://www.nczonline.net/blog/2010/01/26/answering-baranovskiys-javascript-quiz/
- http://dmitry.baranovskiy.com/post/91403200
- https://github.com/yangshun/tech-interview-handbook/blob/master/front-end/interview-questions.md#js-questions
- https://github.com/h5bp/Front-end-Developer-Interview-Questions#js-questions
this
useful hacks