Eu me lembro dos primeiros dias do JavaScript, quando era necessário usar uma função simples para quase tudo, porque os fabricantes de navegadores implementavam recursos de forma diferente, e não apenas recursos básicos, como addEventListener e attachEvent. Os tempos mudaram, mas ainda existem algumas funções muito úteis que cada desenvolvedor deve ter em seu arsenal, para aumentar o desempenho do desenvolvimento e para fins de facilidades funcionais.
debounce
A função debounce pode ser um divisor de águas quando se trata de desempenho impulsionado por eventos. Se você não estiver usando uma função debounce com os eventos scroll, resize e key*, então provavelmente você está fazendo algo errado. Aqui está uma função 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);
A função debounce não permitirá que um callback seja usado mais de uma vez por um determinado período de tempo. Isso é especialmente importante quando a atribuição de uma função de callback para eventos é utilizada com uma frequência de disparo.
poll
Como mencionei em relação à função debounce, às vezes você não consegue se conectar a um evento para verificar seu estado – se o evento não existir, você precisa verificar o seu estado em intervalos:
function poll(fn, callback, errback, timeout, interval) { var endTime = Number(new Date()) + (timeout || 2000); interval = interval || 100; (function p() { // If the condition is met, we're done! if(fn()) { callback(); } // If the condition isn't met but the timeout hasn't elapsed, go again else if (Number(new Date()) < endTime) { setTimeout(p, interval); } // Didn't match and too much time, reject! else { errback(new Error('timed out for ' + fn + ': ' + arguments)); } })(); } // Usage: ensure element is visible poll( function() { return document.getElementById('lightbox').offsetWidth > 0; }, function() { // Done, success callback }, function() { // Error, failure callback } );
O polling tem sido útil na web e continuará sendo no futuro!
once
Há momentos em que você pode preferir que uma determinada funcionalidade só aconteça uma vez, semelhante à maneira como você usaria um evento onload. Este código fornece esse recurso rapidamente:
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
A função once garante que uma determinada função só pode ser chamada uma vez e, assim, evitar a inicialização duplicada!
getAbsoluteUrl
Obter uma URL absoluta de uma cadeia variável de dados não é tão fácil quanto você pensa. Há o construtor URL que pode ser usado para isso, mas ele não funciona se você não fornecer os argumentos necessários (que às vezes você simplesmente não tem). Aqui está um truque simples para obter uma URL absoluta de uma cadeia de entrada:
var getAbsoluteUrl = (function() { var a; return function(url) { if(!a) a = document.createElement('a'); a.href = url; return a.href; }; })(); // Usage getAbsoluteUrl('/something'); // http://davidwalsh.name/something
O elemento “burn” do href cria um objeto com a URL, fornecendo uma URL absoluta no retorno.
isNative
Saber se uma determinada função é nativa ou não pode sinalizar se você poderia substituí-la. Este código vem a calhar para 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, '\\amp;') // 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.*?') + '#039; ); 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 faz um trabalho bem feito!
insertRule
Nós todos sabemos que podemos pegar um NodeList de um seletor (via document.querySelectorAll) e dar a cada um deles um estilo próprio, mas o que é mais eficiente é definir um estilo a um seletor (assim como seria feito 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 se trabalha em um site dinâmico, contendo AJAX pesado. Se você definir o estilo de um seletor, não precisará dar conta de denominar cada elemento que pode corresponder ao seletor (agora ou no futuro).
matchesSelector
Muitas vezes nós validamos a entrada antes de avançar, garantindo um valor verdadeiro, garantindo que os dados de formulários são válidos etc. Mas quantas vezes temos que garantir que um determinado elemento se qualifica para seguir em frente? Você pode usar uma função matchesSelector para validar se um elemento é de um determinado jogo de seletores:
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]')
Agora você tem sete funções JavaScript que todo desenvolvedor deve ter em sua caixa de ferramentas. Tem alguma função muito útil que eu perdi? Por favor, compartilhe!
***
David Walsh faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://davidwalsh.name/essential-javascript-functions