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!