Lembro-me dos primeiros dias do JavaScript, quando era necessário ter uma função simples para quase tudo, porque os fornecedores de navegadores implementavam recursos de forma diferente, e não apenas recursos de ponta, mas recursos básicos, como addEventListener
e attachEvent
. Os tempos mudaram, mas ainda há algumas funções que todo desenvolvedor deve ter em seu arsenal, para fins de desempenho e facilidade funcional.
A função de debounce pode ser um divisor de águas quando se trata de desempenho alimentado por eventos. Se o senhor não estiver usando uma função de debounce com um scroll
, resize
, key*
o senhor provavelmente está fazendo isso errado. Aqui está um debounce
para manter seu código eficiente:
// Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. function debounce(func, wait, immediate) { var timeout; return function() { var context = this, args = arguments; var later = function() { timeout = null; if (!immediate) func.apply(context, args); }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) func.apply(context, args); }; }; // Usage var myEfficientFn = debounce(function() { // All the taxing stuff you do }, 250); window.addEventListener('resize', myEfficientFn);
O debounce
não permitirá que um retorno de chamada seja usado mais de uma vez por determinado período de tempo. Isso é especialmente importante quando se atribui uma função de retorno de chamada a eventos de disparo frequente.
Como mencionei com o debounce
às vezes não é possível conectar-se a um evento para indicar um estado desejado – se o evento não existir, o senhor precisa verificar o estado desejado em intervalos:
// The polling function function poll(fn, timeout, interval) { var endTime = Number(new Date()) + (timeout || 2000); interval = interval || 100; var checkCondition = function(resolve, reject) { // If the condition is met, we're done! var result = fn(); if(result) { resolve(result); } // If the condition isn't met but the timeout hasn't elapsed, go again else if (Number(new Date()) 0; }, 2000, 150).then(function() { // Polling done, now do something else! }).catch(function() { // Polling timed out, handle the error! });
As enquetes são úteis há muito tempo na Web e continuarão a ser no futuro!
Há ocasiões em que você prefere que uma determinada funcionalidade ocorra apenas uma vez, da mesma forma que você usaria um onload
event. Esse código fornece ao senhor essa funcionalidade:
function once(fn, context) { var result; return function() { if(fn) { result = fn.apply(context || this, arguments); fn = null; } return result; }; } // Usage var canOnlyFireOnce = once(function() { console.log('Fired!'); }); canOnlyFireOnce(); // "Fired!" canOnlyFireOnce(); // nada
O once
garante que uma determinada função só possa ser chamada uma vez, evitando assim a inicialização duplicada!
Obter um URL absoluto de uma string variável não é tão fácil quanto o senhor pensa. Há a função URL
mas ele pode dar errado se o usuário não fornecer os argumentos necessários (o que às vezes não é possível). Aqui está um truque suave para obter um URL absoluto a partir de uma entrada de string:
var getAbsoluteUrl = (function() { var a; return function(url) { if(!a) a = document.createElement('a'); a.href = url; return a.href; }; })(); // Usage getAbsoluteUrl('/something'); // https://davidwalsh.name/something
O elemento “queimar” href
lida com o absurdo do URL para o senhor, fornecendo um URL absoluto confiável em troca.
Saber se uma determinada função é nativa ou não pode indicar se o senhor está disposto a substituí-la. Esse código prático pode lhe dar a resposta:
;(function() { // Used to resolve the internal `[[Class]]` of values var toString = Object.prototype.toString; // Used to resolve the decompiled source of functions var fnToString = Function.prototype.toString; // Used to detect host constructors (Safari > 4; really typed array specific) var reHostCtor = /^\[object .+?Constructor\]$/; // Compile a regexp using a common native method as a template. // We chose `Object#toString` because there's a good chance it is not being mucked with. var reNative = RegExp('^' + // Coerce `Object#toString` to a string String(toString) // Escape any special regexp characters .replace(/[.*+?^${}()|[\]\/\\]/g, '\\$&') // Replace mentions of `toString` with `.*?` to keep the template generic. // Replace thing like `for ...` to support environments like Rhino which add extra info // such as method arity. .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$' ); function isNative(value) { var type = typeof value; return type == 'function' // Use `Function#toString` to bypass the value's own `toString` method // and avoid being faked out. ? reNative.test(fnToString.call(value)) // Fallback to a host object check because some environments will represent // things like typed arrays as DOM methods which may not conform to the // normal native pattern. : (value && type == 'object' && reHostCtor.test(toString.call(value))) || false; } // export however you want module.exports = isNative; }()); // Usage isNative(alert); // true isNative(myCustomFunction); // false
A função não é bonita, mas dá conta do recado!
Todos sabemos que podemos pegar um NodeList de um seletor (via document.querySelectorAll
) e atribuir um estilo a cada um deles, mas o mais eficiente é definir esse estilo em um seletor (como o senhor faz em uma folha de estilo):
var sheet = (function() { // Create the <style> tag var style = document.createElement('style'); // Add a media (and/or media query) here if you'd like! // style.setAttribute('media', 'screen') // style.setAttribute('media', 'only screen and (max-width : 1024px)') // WebKit hack :( style.appendChild(document.createTextNode('')); // Add the <style> element to the page document.head.appendChild(style); return style.sheet; })(); // Usage sheet.insertRule("header { float: left; opacity: 0.8; }", 1);
Isso é especialmente útil quando o senhor trabalha em um site dinâmico e com muito AJAX. Se o senhor definir o estilo para um seletor, não precisará levar em conta o estilo de cada elemento que possa corresponder a esse seletor (agora ou no futuro).
Muitas vezes, validamos a entrada antes de avançar, garantindo um valor verdadeiro, garantindo que os dados do formulário sejam válidos etc. Mas com que frequência garantimos que um elemento se qualifica para avançar? O senhor pode usar um matchesSelector
para validar se um elemento é de uma determinada correspondência de seletor:
function matchesSelector(el, selector) { var p = Element.prototype; var f = p.matches || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || function(s) { return [].indexOf.call(document.querySelectorAll(s), this) !== -1; }; return f.call(el, selector); } // Usage matchesSelector(document.getElementById('myDiv'), 'div.someSelector[some-attribute=true]')
Aqui está: sete funções JavaScript que todo desenvolvedor deve ter em sua caixa de ferramentas. O senhor tem alguma função que me escapou? Por favor, compartilhe-a!