Recursos ocultos do JavaScript? [fechadas]

312

Quais "recursos ocultos" do JavaScript você acha que todo programador deveria saber?

Depois de ter visto a excelente qualidade das respostas para as seguintes perguntas, pensei que era hora de solicitar JavaScript.

Embora o JavaScript seja sem dúvida a linguagem mais importante do lado do cliente no momento (basta perguntar ao Google), é surpreendente como a maioria dos desenvolvedores da Web aprecia o quão poderoso ele realmente é.

Binoj Antony
fonte
1
Você não quis dizer "Tendo visto o representante apontar e ver atraído essa outra pergunta, pensei em fazer quase exatamente a mesma pergunta para melhorar a minha"? ;-)
Bobby Jack
1
Claro, pessimista. :) Eu tinha pensado em fazer disso uma pergunta da comunidade. Além disso, depois de obter um certo número de pontos, todos retornos decrescentes.
Allain Lalonde
1
É justo - não parece que você 'precisa' do representante! Acho que só tenho um grande problema com o C # one - não me parece exatamente o tipo de pergunta para a qual este site foi destinado.
Bobby Jack
3
Sim, talvez não, mas achei o conhecimento nas respostas ótimo. Eu acho que seria difícil expor um programador C # médio a tudo isso em um só lugar, se não fosse por isso. Levaria anos jogando com ele para chegar à mesma lista de vitórias duras.
Allain Lalonde
7
Estou escrevendo JavaScript profissionalmente há 10 anos e aprendi uma coisa ou três com esse tópico. Obrigado Alan!
Andrew Hedges

Respostas:

373

Você não precisa definir nenhum parâmetro para uma função. Você pode apenas usar o argumentsobjeto de matriz da função .

function sum() {
    var retval = 0;
    for (var i = 0, len = arguments.length; i < len; ++i) {
        retval += arguments[i];
    }
    return retval;
}

sum(1, 2, 3) // returns 6
Mark Cidade
fonte
117
Vale ressaltar que, embora os argumentos funcionem como uma matriz, não é uma matriz javascript real - é apenas um objeto. Então você não pode fazer join (), pop (), push (), fatia () e assim por diante. (Você pode convertê-lo para uma matriz real, se você quiser: "var argArray = Array.prototype.slice.call (argumentos);")
Jacob Mattison
51
Também vale a pena notar que o acesso ao objeto Arguments é relativamente caro - os melhores exemplos estão nas noites noturnas do Safari, Firefox e Chrome, onde apenas fazer referência ao argumentsobjeto torna a chamada de uma função muito mais lenta - por exemplo. argumentos if (false); vai doer perf.
18710
48
Na mesma linha, os argumentos têm uma propriedade "callee", que é a própria função atual. Isso permite fazer recursão com funções anônimas, legal!
Vincent Robert
4
@ Nathan "f (x, y, z)" parece melhor que "f ([x, y, z])".
Mark Cidade
16
@ Vincent Robert: observe que arguments.calleeestá sendo preterido.
ken
204

Eu poderia citar a maior parte do excelente livro de Douglas Crockford, JavaScript: The Good Parts .

Mas vou levar apenas um para você, sempre use ===e em !==vez de ==e!=

alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true

==não é transitivo. Se você usá- ===lo, daria false para todas essas instruções conforme o esperado.

Martin Clarke
fonte
29
É uma pena que tantas pessoas pensem que Crockford é onisciente. Concedido, o cara é direito sobre a marca com a maioria de suas críticas, mas eu parar antes de dar as coisas dele um endosso cobertor como tantos devs fazer ...
Jason Bunting
21
Eu segundo o aviso de Jason. O livro em si é muito interessante, e ele faz dar um monte de bons conselhos, mas DC está longe demais convencido de que sua maneira de fazer as coisas é a única maneira correta, tudo o resto é "defeituoso". Se você quiser alguns exemplos, veja as respostas dele no JSLint Yahoo Group.
Zilk 28/10/08
30
Use === em vez de == é um bom conselho se você estiver confuso com a digitação dinâmica e apenas quiser que seja "realmente" igual. Aqueles de nós que entendem a digitação dinâmica podem continuar usando == para situações nas quais sabemos que queremos transmitir, como em 0 == '' ou 0 == '0'.
thomasrutter
20
Bem, == e === não são sobre digitação dinâmica. == digita coersão, que é uma fera diferente. Se você sabe que deseja converter para string / number / etc, deve fazê-lo explicitamente.
Rene Saarsoo
15
Eu acho que a parte mais assustadora ==é '\n\t\r ' == 0=> true...: D
Shrikant Sharat
189

As funções são cidadãos de primeira classe em JavaScript:

var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };

var sum = function(x,y,z) {
  return x+y+z;
};

alert( passFunAndApply(sum,3,4,5) ); // 12

Técnicas de programação funcional podem ser usadas para escrever javascript elegante .

Particularmente, funções podem ser passadas como parâmetros, por exemplo, Array.filter () aceita um retorno de chamada:

[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]

Você também pode declarar uma função "privada" que existe apenas no escopo de uma função específica:

function PrintName() {
    var privateFunction = function() { return "Steve"; };
    return privateFunction();
}
Gulzar Nazim
fonte
3
Existem três maneiras de criar funções em javascript: function sum (x, y, z) {return (x + y + z); } e var sum = new Function ("x", "y", "z", "return (x + y + z);"); são os outros caminhos.
Marius
6
O conceito de funções como dados definitivamente ganha grandes pontos no meu livro.
Jason Bunting
Acabei de atualizar o exemplo para mostrar como usar uma função "particular" que existe apenas no escopo de uma função específica.
Chris Pietschmann 10/10/08
new Function()é tão mau quanto eval. Não use.
Nicolás
11
não tenho certeza se esse é um recurso oculto ... mais como um recurso principal.
precisa
162

Você pode usar o operador in para verificar se existe uma chave em um objeto:

var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true

Se você achar os literais de objeto muito feios, poderá combiná-lo com a dica de função sem parâmetros:

function list()
 { var x = {};
   for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
   return x
 }

 5 in list(1,2,3,4,5) //true
Mark Cidade
fonte
22
Não é tão inteligente, que verifica se uma chave está presente, não se um valor está. x na lista; só funciona porque x [1]! = null, não porque o valor 1 esteja lá.
Armin Ronacher
1
Eu não usei a técnica há algum tempo, então esqueci que realmente usei literais de objetos antes. Obrigado pela correção.
Mark Cidade
34
Além disso, tenha cuidado: o operador in também testa a cadeia de protótipos! Se alguém colocar uma propriedade chamada '5' no Object.prototype, o segundo exemplo retornará true, mesmo que você chame '5 na lista (1, 2, 3, 4)' ... É melhor usar o hasOwnProperty method: list (1, 2, 3, 4) .hasOwnProperty (5) retornará false, mesmo se Object.prototype tiver uma propriedade '5'.
287
3
Para a solução mais geral, aquela que pode testar se um Objeto tem sua própria propriedade, mesmo que seja nomeado "hasOwnProperty", você deve ir até: Object.prototype.hasOwnProperty.call (objeto, nome) ;
Kris Kowal
1
@Kris, a não ser que alguém substitui Object.prototype.hasOwnProperty;)
Nick
153

Atribuindo valores padrão a variáveis

Você pode usar o lógico ou o operador ||em uma expressão de atribuição para fornecer um valor padrão:

var a = b || c;

A avariável terá o valor de csomente se bé Falsas (se é null, false, undefined, 0, empty string, ou NaN), caso contrário, airá obter o valor de b.

Isso geralmente é útil em funções, quando você deseja atribuir um valor padrão a um argumento, caso não seja fornecido:

function example(arg1) {
  arg1 || (arg1 = 'default value');
}

Exemplo de fallback do IE em manipuladores de eventos:

function onClick(e) {
    e || (e = window.event);
}

Os seguintes recursos de idioma estão conosco há muito tempo, todas as implementações de JavaScript os suportam, mas eles não faziam parte da especificação até o ECMAScript 5th Edition :

A debuggerdeclaração

Descrito em: § 12.15 A declaração do depurador

Esta declaração permite que você coloque pontos de interrupção programaticamente em seu código apenas por:

// ...
debugger;
// ...

Se um depurador estiver presente ou ativo, ele fará com que ele se quebre imediatamente, exatamente nessa linha.

Caso contrário, se o depurador não estiver presente ou ativo, esta instrução não terá efeito observável.

Literais de sequência de linhas múltiplas

Descrito em: § 7.8.4 Literais de cordas

var str = "This is a \
really, really \
long line!";

Você tem que ter cuidado porque o personagem ao lado do \ deve ser um terminador de linha, se você tem um espaço após o \por exemplo, o código vai olhar exatamente o mesmo, mas vai levantar um SyntaxError.

CMS
fonte
28
Não se for nulo, se for considerado falso. a = 0 || 42; lhe dará 42. Isso é comparável ao do Python ou não do c #? operador. Se você deseja o comportamento C #, faça a = (b === null)? c: b;
Armin Ronacher
Também funciona no Visual Studio, se você desenvolver no ASP.NET :)
chakrit
2
Eu gostaria que houvesse o apropriado || apenas indefinido. Fui mordido por isso hoje por 0, pois queria criar emulação de método sobrecarregado, para que o último argumento fosse opcional e um valor padrão fosse usado.
egaga
+1 neste truque é utilizado pelo snippet padrão do Google Analytics. `var _gaq = _gaq || []; `; impede que usuários superzelosos substituam seu próprio trabalho.
Yahel
2
Eu não sabia sobre a técnica literal de cadeias multilinhas. Isso é fantástico, obrigado.
Charlie Flowers
145

O JavaScript não tem escopo de bloco (mas tem fechamento, então vamos chamá-lo mesmo?).

var x = 1;
{
   var x = 2;
}
alert(x); // outputs 2
Eugene Yokota
fonte
3
Essa é boa. É uma diferença muito importante da maioria dos idiomas C.
Martin Clarke
9
Você sempre pode fazer "var tmp = function () {/ * bloco escopo * /} ();". A sintaxe é feia, mas funciona.
Joeri Sebrechts
3
Ou você pode usar "let" se for Firefox apenas: stackoverflow.com/questions/61088/...
Eugene Yokota
10
ou apenas: (function () {var x = 2;}) (); alerta (tipo x); // indefinido
Pim Jager
@Pim: JSLint diz: "Mova a invocação para os parênteses que contêm a função.". Junto com "esperado exatamente um espaço entre 'função' e '('.".
Hello71
144

Você pode acessar as propriedades do objeto em []vez de.

Isso permite procurar uma propriedade que corresponda a uma variável.

obj = {a:"test"};
var propname = "a";
var b = obj[propname];  // "test"

Você também pode usar isso para obter / definir propriedades do objeto cujo nome não é um identificador legal.

obj["class"] = "test";  // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.

Algumas pessoas não sabem disso e acabam usando eval () como este, o que é uma péssima ideia :

var propname = "a";
var a = eval("obj." + propname);

Isso é mais difícil de ler, mais difícil de encontrar erros (não é possível usar o jslint), é mais lento para executar e pode levar a explorações do XSS.

Patrick
fonte
eval é mau, embora raramente necessária
Doug Domeny
Eu nunca uso eval e lembro quando descobri isso. Isso me fez muito feliz.
Em resumo, as propriedades do objeto podem ser acessadas por meio de notação de ponto e de subscrito #
Russ Cam
9
É interessante notar que a referência a pontos é, na verdade, açúcar de sintaxe para o bracketref. foo.bar, de acordo com as especificações de qualquer maneira, se comporta exatamente como foo["bar"]. Observe também que tudo é uma propriedade de string. mesmo quando você faz acesso à matriz, array[4]o 4 é convertido em um string (novamente, pelo menos de acordo com ECMAScript v3 especificação)
Claudiu
Eu acho que todo programador de JS deve saber disso.
Cem Kalyoncu
144

Se você estiver pesquisando no Google uma referência decente sobre JavaScript sobre um determinado tópico, inclua a palavra-chave "mdc" na sua consulta e seus primeiros resultados serão do Mozilla Developer Center. Eu não carrego nenhuma referência offline ou livros comigo. Eu sempre uso o truque de palavras-chave "mdc" para obter diretamente o que estou procurando. Por exemplo:

Google: javascript array sort mdc
(na maioria dos casos, você pode omitir "javascript")

Atualização: O Mozilla Developer Center foi renomeado para Mozilla Developer Network . O truque da palavra-chave "mdc" ainda funciona, mas em breve poderemos ter que começar a usar "mdn" .

Ates Goral
fonte
50
Uau, ótimo recurso. Instantaneamente melhor do que o w3schools de baixa qualidade ...
DisgruntledGoat
11
Você nem precisa pesquisar no Google, se estiver no Firefox: digite "array mdc" na barra de endereços e pressione Enter.
Sasha Chedygov 10/04/10
2
a melhor parte é a forma como esta questão de estouro de pilha está na primeira página de resultados :)
Jiaaro
5
Como proposta: o promoverjs.com , uma iniciativa popular de SEO para impulsionar os resultados do MDC ainda mais nos resultados de pesquisa do Google.
Yahel
3
Agora é o centro doc do MDN, de modo que o 'mdc' palavra-chave ainda é válida :)
Aleadam
143

Talvez um pouco óbvio para alguns ...

Instale o Firebug e use console.log ("olá"). Muito melhor do que usar o alert random (); é o que me lembro de ter feito há alguns anos.

qui
fonte
12
Apenas não esqueça de remover as instruções do console antes de liberar seu código para outras pessoas que podem não ter o Firebug instalado.
Chris Noe
161
função log (msg) {if (console) console.log (msg) else alert (msg)}
Josh
4
Melhor ainda, preceda as instruções de log com ';;;' e o minify cuida disso para você. (Pelo menos, o Perl uso módulo I tem essa característica, e afirma que é comum.)
Kev
10
Josh: Isso não vai funcionar, pois o console não está definido. Você pode verificar o tipo de console! == "undefined" ou window.console.
Eli Grey
23
Sempre inclua: if (typeof ('console') == 'undefined') {console = {log: function () {}}; }, você pode continuar usando o console.log e isso simplesmente não faz nada.
gregmac 2/09/09
120

Métodos particulares

Um objeto pode ter métodos particulares.

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    // A private method only visible from within this constructor
    function calcFullName() {
       return firstName + " " + lastName;    
    }

    // A public method available to everyone
    this.sayHello = function () {
        alert(calcFullName());
    }
}

//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();

// This fails since the method is not visible from this scope
alert(person1.calcFullName());
Allain Lalonde
fonte
16
Essa não é realmente uma função privada - é mais uma variável de função em um escopo local.
Keith
6
É verdade, mas em todas as definições operacionais, posso pensar que é um método. É um bloco de código com um nome que tem acesso ao estado da instância e só pode ser visto por essa instância. Qual é a sua definição de método privado?
Allain Lalonde
14
@Zach, exatamente! Depois de passar anos trabalhando com linguagens OO baseadas em classes, é fácil esquecer que elas são apenas uma implementação dos conceitos de OO. Claro, as várias bibliotecas que tentam OO baseada em quasi-classe enfiar JS não quer ajudar ...
Shog9
5
Imaginando, person1 tem um blog de direito? ;-)
travis
4
+1 para a referência de desenvolvimento interrompido
Domenic 28/02
99

Também mencionado em "Javascript: The Good Parts" de Crockford:

parseInt()é perigoso. Se você passar uma string sem informar a base correta, poderá retornar números inesperados. Por exemplo, parseInt('010')retorna 8, não 10. Passar uma base para parseInt faz com que funcione corretamente:

parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.
bluefish101
fonte
13
Ao fazer revisões de código, sempre procure por essa. Deixar o ", 10" é um erro comum que passa despercebido na maioria dos testes.
Doug Domeny
Fui queimado pela questão do radix anos atrás e nunca esqueci algo tão contra-intuitivo como tal. Uma grande coisa a salientar, pois isso fará você se perguntar por um tempo.
JamesEggers
4
Por que não usar Math.floorou Number? 10 === Math.floor("010"); 10 === Number("010");carros alegóricos:42 === Math.floor("42.69"); 42.69 === Number("42.69");
apenas alguém
1
@ Infinidade Se ainda não tiver uma resposta publicada, você deve. Eu não fazia ideia de que era tão simples substituir esse comportamento da função interna. Obviamente, deve-se olhar um pouco mais de perto os pacotes de código emprestados de outros sites. Essa parseIntfunção inofensiva poderia facilmente ser feita para fazer algo não tão inofensivo.
bob-the-destroyer
6
@ Infinidade: que tal redefinir o fn para destacar o 'erro de codificação'? __parseInt = parseInt; parseInt = function (str, base) { if (!base) throw new Error(69, "All your base belong to us"); return __parseInt(str, base); }
JBRWilkinson
97

Funções são objetos e, portanto, podem ter propriedades.

fn = função (x) {
   // ...
}

fn.foo = 1;

fn.next = função (y) {
  //
}
VolkerK
fonte
13
Esta é uma dica muito útil. Por exemplo, você pode definir valores padrão como uma propriedade da função. Por exemplo: myfunc.delay = 100; Em seguida, os usuários podem alterar o valor padrão e todas as chamadas de função usarão o novo valor padrão. Por exemplo: myfunc.delay = 200; myfunc ();
BarelyFitz #
Útil ... e perigoso!
palswim
Parece desleixado, por que usar isso em vez de uma variável?
precisa
1
@instantsetsuna: Por que ter outra variável separada ? Como de costume, isso se resume a "usá-lo quando necessário / útil" ;-)
VolkerK
91

Eu teria que dizer funções auto-executáveis.

(function() { alert("hi there");})();

Como o Javascript não tem escopo de bloco , você pode usar uma função de execução automática se desejar definir variáveis ​​locais:

(function() {
  var myvar = 2;
  alert(myvar);
})();

Aqui, myvaris não interfere ou polui o escopo global e desaparece quando a função termina.

thomasrutter
fonte
2
Para que isso é útil? Você obtém os mesmos resultados ao colocar o alerta fora da função.
PotatoEngineer 20/03/09
7
Não é sobre o alerta, é sobre definir e executar uma função ao mesmo tempo. Você pode fazer com que a função de execução automática retorne um valor e passe a função como um parâmetro para outra função.
31499 ScottKoon
5
@Paul é bom para encapsulamento.
Mike Robinson
22
Também é bom para o escopo do bloco.
23711 Jim Hunziker
24
Sim, coloco todos os meus .jsarquivos em uma função de execução automática anônima e anexo qualquer windowobjeto que eu queira que seja acessível globalmente ao objeto. Impede a poluição global do espaço para nome.
Cdmckay #
83

Saiba quantos parâmetros são esperados por uma função

function add_nums(num1, num2, num3 ){
    return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.

Saiba quantos parâmetros são recebidos pela função

function add_many_nums(){
    return arguments.length;
}    
add_many_nums(2,1,122,12,21,89); //returns 6
pramodc84
fonte
23
Nunca soube da primeira parte. Agradável!
Mcjabberz 17/09/09
1
Da mesma forma, você pode descobrir com quantos argumentos uma função está esperando function.length.
Xavi
6
@Xavi que é primeira parte da resposta
pramodc84
79

Aqui estão algumas coisas interessantes:

  • Comparando NaNcom qualquer coisa (mesmo NaN) é sempre falsa, que inclui ==, <e >.
  • NaN Significa Não é um número, mas se você solicitar o tipo, ele realmente retornará um número.
  • Array.sort pode assumir uma função comparadora e é chamado por um driver do tipo quicksort (depende da implementação).
  • A expressão regular "constantes" pode manter o estado, como a última coisa em que corresponderam.
  • Algumas versões do JavaScript permitem acesso $0, $1, $2membros em um regex.
  • nullé diferente de qualquer outra coisa. Não é um objeto, um booleano, um número, uma string, nem undefined. É um pouco como uma "alternativa" undefined. (Nota typeof null == "object":)
  • No contexto mais externo, thisgera o objeto [Global], de outro modo inominável.
  • Declarar uma variável com var, em vez de apenas confiar na declaração automática da variável, dá ao tempo de execução uma chance real de otimizar o acesso a essa variável
  • A withconstrução destruirá essas otimizações
  • Os nomes de variáveis ​​podem conter caracteres Unicode.
  • As expressões regulares do JavaScript não são realmente regulares. Eles são baseados nas regexs do Perl, e é possível construir expressões com lookaheads que levam muito, muito tempo para serem avaliadas.
  • Os blocos podem ser rotulados e usados ​​como alvos de break. Os loops podem ser rotulados e usados ​​como destino de continue.
  • Matrizes não são esparsas. Definir o 1000º elemento de uma matriz vazia, caso contrário, deve preenchê-lo undefined. (depende da implementação)
  • if (new Boolean(false)) {...} irá executar o {...}bloco
  • Os mecanismos de expressão regular do Javascript são específicos da implementação: por exemplo, é possível escrever expressões regulares "não portáveis".

[atualizado um pouco em resposta a bons comentários; por favor, veja os comentários]

David Leonard
fonte
5
null é realmente um objeto (especial). typeof nullretorna "objeto".
Ates Goral 01/10/08
4
Você também pode obter o objeto [Global] de qualquer lugar como este: var glb = function () {return this; } ();
Zilk 28/10/08
2
O objeto global em javascript em um navegador é o objeto de janela. Quando no escopo global, está fazendo: window.a == a;
Pim Jager
8
"Matrizes não são esparsas" depende da implementação. Se você definir o valor de [1000] e observar [999], sim, é undefined, mas esse é apenas o valor padrão que você obtém ao procurar um índice que não existe. Se você selecionou um [2000], isso também seria undefined, mas isso não significa que você alocou memória para ele ainda. No IE8, algumas matrizes são densas e outras esparsas, dependendo da sensação do mecanismo JScript no momento. Leia mais aqui: blogs.msdn.com/jscript/archive/2008/04/08/…
Chris Nielsen
2
@Ates e @SF: typeof retorna "objeto" para uma variedade de tipos diferentes. Porém, depois que você souber como ele funciona e quais tipos se identificam como "objeto", é pelo menos confiável e consistente em sua implementação.
thomasrutter
77

Sei que estou atrasado para a festa, mas não acredito +que a utilidade do operador não tenha sido mencionada além de "converter qualquer coisa em um número". Talvez seja assim que oculta uma característica?

// Quick hex to dec conversion:
+"0xFF";              // -> 255

// Get a timestamp for now, the equivalent of `new Date().getTime()`:
+new Date();

// Safer parsing than parseFloat()/parseInt()
parseInt("1,000");    // -> 1, not 1000
+"1,000";             // -> NaN, much better for testing user input
parseInt("010");      // -> 8, because of the octal literal prefix
+"010";               // -> 10, `Number()` doesn't parse octal literals 

// A use case for this would be rare, but still useful in cases
// for shortening something like if (someVar === null) someVar = 0;
+null;                // -> 0;

// Boolean to integer
+true;                // -> 1;
+false;               // -> 0;

// Other useful tidbits:
+"1e10";              // -> 10000000000
+"1e-4";              // -> 0.0001
+"-12";               // -> -12

Obviamente, você pode fazer tudo isso usando Number(), mas o +operador é muito mais bonito!

Você também pode definir um valor de retorno numérico para um objeto substituindo o valueOf()método do protótipo . Qualquer conversão numérica realizada nesse objeto não resultará NaN, mas o valor de retorno do valueOf()método:

var rnd = {
    "valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd;               // -> 442;
+rnd;               // -> 727;
+rnd;               // -> 718;
Andy E
fonte
Você pode fazer simplesmente 0xFFetc., não há necessidade +"0xFF".
precisa saber é o seguinte
9
@ Nyuszika7H: você está meio que perdendo o argumento, que está coagindo outros primitivos e objetos a números. Claro que você pode simplesmente escrever 0xFF, da mesma maneira que você pode escrever, em 1vez de+true . Estou sugerindo que você possa usar +("0x"+somevar)como alternativa parseInt(somevar, 16), se quiser.
Andy E
75

" Métodos de extensão em JavaScript " através da propriedade prototype.

Array.prototype.contains = function(value) {  
    for (var i = 0; i < this.length; i++) {  
        if (this[i] == value) return true;  
    }  
    return false;  
}

Isso adicionará um containsmétodo a todos os Arrayobjetos. Você pode chamar esse método usando esta sintaxe

var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");
colher
fonte
18
Isso geralmente é considerado uma má idéia, porque outro código (não o seu) pode fazer suposições sobre o objeto Array.
22420 Chris Noe
39
Também é geralmente considerado uma má idéia fazer suposições sobre o objeto Array. :(
eyelidlessness
Uhmmmm .. javascript 1.6 array extras? índice de? tocando alguns sinos?
Breton
2
@Breton: Não é algo específico para a classe Array, é apenas um exemplo. Eu uso isso para estender o novo Date (). ToString (); , permitindo usar uma string de máscara. Qualquer objeto pode ser estendido e todas as instâncias recebem o novo método.
Esteban Küber 30/06/09
1
@ Matias: não se trata do DOM.
dólmen
60

Para remover corretamente uma propriedade de um objeto, você deve excluir a propriedade em vez de apenas defini-la como indefinida :

var obj = { prop1: 42, prop2: 43 };

obj.prop2 = undefined;

for (var key in obj) {
    ...

A propriedade prop2 ainda fará parte da iteração. Se você quiser se livrar completamente do prop2 , faça:

delete obj.prop2;

A propriedade prop2 não aparecerá mais quando você estiver iterando pelas propriedades.

Ates Goral
fonte
3
Observe que a instrução delete não está isenta de peculiaridades específicas do navegador. Por exemplo, isso falhará com um grande erro se você tentar no IE e o objeto não for um objeto JS nativo (mesmo ao excluir uma propriedade que você adicionou). Também não se destina a excluir uma variável, como em excluir myvar; mas acho que funciona em alguns navegadores. O código na resposta acima parece bastante seguro.
precisa saber é o seguinte
a propósito, indefinido também pode ser uma variável! Tente var undefined = "something"
Johann Philipp Strathausen
57

with.

É raramente usado e, francamente, raramente útil ... Mas, em circunstâncias limitadas, ele tem seus usos.

Por exemplo: literais de objeto são bastante úteis para configurar rapidamente propriedades em um novo objeto. Mas e se você precisar alterar metade das propriedades em um objeto existente?

var user = 
{
   fname: 'Rocket', 
   mname: 'Aloysus',
   lname: 'Squirrel', 
   city: 'Fresno', 
   state: 'California'
};

// ...

with (user)
{
   mname = 'J';
   city = 'Frostbite Falls';
   state = 'Minnesota';
}

Alan Storm ressalta que isso pode ser um pouco perigoso: se o objeto usado como contexto não tiver uma das propriedades atribuídas, ele será resolvido no escopo externo, possivelmente criando ou substituindo uma variável global. Isso é especialmente perigoso se você estiver acostumado a escrever código para trabalhar com objetos em que as propriedades com valores padrão ou vazios são deixadas indefinidas:

var user = 
{
   fname: "John",
// mname definition skipped - no middle name
   lname: "Doe"
};

with (user)
{
   mname = "Q"; // creates / modifies global variable "mname"
}

Portanto, é provavelmente uma boa idéia evitar o uso da withdeclaração para essa tarefa.

Veja também: Existem usos legítimos para a declaração "with" do JavaScript?

Shog9
fonte
29
A sabedoria convencional com a afirmação deve ser evitada. Se o objeto de usuário não tivesse uma das propriedades mencionadas, a variável fora do pseudo-escopo do bloco with seria modificada. Dessa forma, há erros. Mais informações em yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Alan Storm
1
Shog, as objeções não são sobre variáveis ​​com erros de ortografia, são sobre olhar para um bloco de código e poder dizer com certeza o que qualquer linha específica nesse bloco faz. Como os objetos Javascript são muito dinâmicos, você não pode dizer com certeza quais propriedades / membros ele possui a qualquer momento.
Alan Storm
2
Amém - se eu vi a declaração "with" em qualquer JS que encontrei, a eliminaria e questionaria o desenvolvedor que a escreveu para ter certeza de que ele sabia por que não era uma boa coisa usá-la ... "recurso oculto?" Mais como "recurso repugnante".
Jason Bunting
1
considere uma cadeia mais complexa abcd "com (abc) {d.foo = bar;} é poderosa e não inerentemente propensa a erros. A chave é reduzir a raiz um nível acima. E com erros de ortografia de um nome de variável? Você está apresentando um bug se você fizer isso , sempre que você fazê-lo, independentemente de "com".
annakata
4
Douglas Crockford disse recentemente que "com" é uma das piores partes do JavaScript em um .NET Rocks! podcast.
núcleo
51

Métodos (ou funções) podem ser chamados em objetos que não são do tipo para o qual foram projetados para trabalhar. É ótimo chamar métodos nativos (rápidos) em objetos personalizados.

var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });

Este código falha porque listNodesnão é umArray

Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);

Esse código funciona porque listNodesdefine propriedades do tipo matriz suficientes (comprimento, [] operador) para serem usadas sort().

Vincent Robert
fonte
43

A herança prototípica (popularizada por Douglas Crockford) revoluciona completamente a maneira como você pensa sobre muitas coisas em Javascript.

Object.beget = (function(Function){
    return function(Object){
        Function.prototype = Object;
        return new Function;
    }
})(function(){});

É um assassino! Pena como quase ninguém a usa.

Permite "gerar" novas instâncias de qualquer objeto, estendê-las, mantendo um link de herança prototípica (ao vivo) para suas outras propriedades. Exemplo:

var A = {
  foo : 'greetings'
};  
var B = Object.beget(A);

alert(B.foo);     // 'greetings'

// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo);     // 'hello'

A.bar = 'world';
alert(B.bar);     // 'world'


// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo);     // 'hello'

B.bar = 'universe';
alert(A.bar);     // 'world'
Már Örlygsson
fonte
42

Alguns chamariam isso de questão de gosto, mas:

aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }

O operador trinário pode ser encadeado para agir como o de Scheme (cond ...):

(cond (predicate  (action  ...))
      (predicate2 (action2 ...))
      (#t         default ))

pode ser escrito como ...

predicate  ? action( ... ) :
predicate2 ? action2( ... ) :
             default;

Isso é muito "funcional", pois ramifica seu código sem efeitos colaterais. Então, em vez de:

if (predicate) {
  foo = "one";
} else if (predicate2) {
  foo = "two";
} else {
  foo = "default";
}

Você pode escrever:

foo = predicate  ? "one" :
      predicate2 ? "two" :
                   "default";

Também funciona bem com recursão :)

Andrey Fedorov
fonte
Eu gosto da sintaxe de predicado que você fornece. Eu nunca pensei em encadear assim. arrumado.
Allain Lalonde
2
O JavaScript possui uma instrução switch (). :-)
staticsan
Eu não sou um grande fã de declarações de switch - eles são um artefato de C, não de programação funcional. No meu exemplo, uma instrução switch ainda precisaria de três instruções separadas, todas começando com "foo =" - repetição desnecessária óbvia.
Andrey Fedorov
14
Eu, por exemplo, saúdo o operador ternário.
thomasrutter
8
Ao reler, gostaria de salientar que isso não é "fazer o código parecer com outra linguagem", mas sim simplificar o significado semântico do código: quando você está tentando dizer "defina foo como um dos três" coisas ", essa é uma afirmação que deve começar com" foo = ... ", não" se ".
Andrey Fedorov
41

Números também são objetos. Então você pode fazer coisas legais como:

// convert to base 2
(5).toString(2) // returns "101"

// provide built in iteration
Number.prototype.times = function(funct){
  if(typeof funct === 'function') {
    for(var i = 0;i < Math.floor(this);i++) {
      funct(i);
    }
  }
  return this;
}


(5).times(function(i){
  string += i+" ";
});
// string now equals "0 1 2 3 4 "

var x = 1000;

x.times(function(i){
  document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document
Zach
fonte
AMD! Eu não sabia sobre toString (radix) ...
Ates Goral
1
Essa implementação de timesnão é eficiente: Math.flooré chamada sempre, em vez de apenas uma vez.
dólmen
33

Que tal fechamentos em JavaScript (semelhante aos métodos anônimos no C # v2.0 +). Você pode criar uma função que cria uma função ou "expressão".

Exemplo de fechamentos :

//Takes a function that filters numbers and calls the function on 
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
  var filteredNumbers = [];

  for (var index = 0; index < numbers.length; index++)
  {
    if (filterFunction(numbers[index]) == true)
    {
      filteredNumbers.push(numbers[index]);
    }
  }
  return filteredNumbers;
}

//Creates a function (closure) that will remember the value "lowerBound" 
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
  return function (numberToCheck) {
    return (numberToCheck > lowerBound) ? true : false;
  };
}

var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];

var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);

numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);

numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);
Tyler
fonte
1
não tenho certeza, mas posso retornar (numberToCheck> lowerBound)? verdadeiro falso; simplesmente torne-se return (numberToCheck> lowerBound); apenas tentando aumentar meu entendimento ...
davidsleeps
4
Eu diria funções anônimas em C # são equivalentes de encerramentos, não o contrário :)
vava
11
Encerramentos e funções anônimas são conceitos distintos e separados. Que funções podem ser criadas sem serem nomeadas está tendo funções anônimas. O fato de uma variável no escopo 'criar' estar vinculada à função criada é um fechamento. Em resumo, um fechamento é mais como uma variável global oculta.
slebetman
1
Isso é verdade. Somente quando métodos anônimos usam uma variável do escopo de criação é semelhante a um fechamento. Eu atualizei o inglês na resposta. Ainda deixa algo a desejar, mas estou perdido para o inglês correto.
Tyler
2
Eu não acho que este seja o melhor ou mais fácil de entender exemplo do que é um fechamento. Apenas dizendo. O ponto principal de um fechamento é que, mesmo quando várias variáveis ​​parecem "sair do escopo", elas ainda podem permanecer disponíveis para uma função que foi originalmente definida nesse escopo. No exemplo acima, isso significa que a variável lowerBound ainda está acessível por essa função interna anônima, mesmo quando a função externa buildGeavyThanFunction termina.
thomasrutter
32

Você também pode estender (herdar) classes e substituir propriedades / métodos usando a colher de cadeia de protótipo16 mencionada.

No exemplo a seguir, criamos uma classe Pet e definimos algumas propriedades. Também substituímos o método .toString () herdado de Object.

Depois disso, criamos uma classe Dog que estende Pet e substitui o método .toString () novamente alterando seu comportamento (polimorfismo). Além disso, adicionamos outras propriedades à classe filho.

Depois disso, verificamos a cadeia de herança para mostrar que Dog ainda é do tipo Dog, do tipo Pet e do tipo Object.

// Defines a Pet class constructor 
function Pet(name) 
{
    this.getName = function() { return name; };
    this.setName = function(newName) { name = newName; };
}

// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function() 
{
    return 'This pets name is: ' + this.getName();
};
// end of class Pet

// Define Dog class constructor (Dog : Pet) 
function Dog(name, breed) 
{
    // think Dog : base(name) 
    Pet.call(this, name);
    this.getBreed = function() { return breed; };
}

// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();

// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;

// Now we override Pet.prototype.toString
Dog.prototype.toString = function() 
{
    return 'This dogs name is: ' + this.getName() + 
        ', and its breed is: ' + this.getBreed();
};
// end of class Dog

var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);

// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true

Ambas as respostas a essa pergunta foram códigos modificados a partir de um ótimo artigo do MSDN de Ray Djajadinata.

Tyler
fonte
31

Você pode capturar exceções, dependendo do tipo. Citado pelo MDC :

try {
   myroutine(); // may throw three exceptions
} catch (e if e instanceof TypeError) {
   // statements to handle TypeError exceptions
} catch (e if e instanceof RangeError) {
   // statements to handle RangeError exceptions
} catch (e if e instanceof EvalError) {
   // statements to handle EvalError exceptions
} catch (e) {
   // statements to handle any unspecified exceptions
   logMyErrors(e); // pass exception object to error handler
}

NOTA: As cláusulas de captura condicional são uma extensão do Netscape (e, portanto, do Mozilla / Firefox) que não faz parte da especificação do ECMAScript e, portanto, não pode ser invocada, exceto em navegadores específicos.

Ionuț G. Stan
fonte
29
Eu não poderia ajudá-la: catch (-me se YOUCAN)
Ates Goral
6
Leia a nota da página do MDC que você citou: as cláusulas de captura condicional são uma extensão do Netscape (e, portanto, do Mozilla / Firefox) que não faz parte da especificação do ECMAScript e, portanto, não pode ser invocada, exceto em navegadores específicos.
22610 Jason S
31

Em cima da minha cabeça...

Funções

argumentos.callee refere-se à função que hospeda a variável "argumentos", para que possa ser usada para processar funções anônimas:

var recurse = function() {
  if (condition) arguments.callee(); //calls recurse() again
}

Isso é útil se você quiser fazer algo assim:

//do something to all array items within an array recursively
myArray.forEach(function(item) {
  if (item instanceof Array) item.forEach(arguments.callee)
  else {/*...*/}
})

Objetos

Uma coisa interessante sobre os membros do objeto: eles podem ter qualquer string como nome:

//these are normal object members
var obj = {
  a : function() {},
  b : function() {}
}
//but we can do this too
var rules = {
  ".layout .widget" : function(element) {},
  "a[href]" : function(element) {}
}
/* 
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
  var elements = document.querySelectorAll(rules[item]);
  for (var e, i = 0; e = elements[i++];) rules[item](e);
}

Cordas

String.split pode usar expressões regulares como parâmetros:

"hello world   with  spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]

String.replace pode usar uma expressão regular como parâmetro de pesquisa e uma função como parâmetro de substituição:

var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"
Leo
fonte
O que você menciona ... Eles são implementados em todos os navegadores?
Cllpse 28/09/08
4
Não. Tenho certeza de que Mosaic não possui a maioria deles.
jsight
2
Os recursos javascript, sim, são implementados em todos os principais navegadores (IE6 / 7, FF2 / 3, Opera 9+, Safari2 / 3 e Chrome). O document.querySelectorAll ainda não é suportado em todos os navegadores (é a versão W3C dos $ () do JQuery e dos $$ () do Prototype
Leo
6
arguments.calleeé obsoleto e irá lançar e exceção no ECMAScript 5.
Hello71
não é bem verdade. Uma chave de objeto não pode (ou melhor, não deveria) usar a cadeia "hasOwnProperty" como um nome, pois isso substituiria o método de objeto incorporado.
Breton
29

Você pode usar objetos em vez de alternar na maioria das vezes.

function getInnerText(o){
    return o === null? null : {
        string: o,
        array: o.map(getInnerText).join(""),
        object:getInnerText(o["childNodes"])
    }[typeis(o)];
}

Atualização: se você está preocupado com a ineficiência dos casos em avaliar antecipadamente (por que você está preocupado com a eficiência no início do design do programa?), Pode fazer algo assim:

function getInnerText(o){
    return o === null? null : {
        string: function() { return o;},
        array: function() { return o.map(getInnerText).join(""); },
        object: function () { return getInnerText(o["childNodes"]; ) }
    }[typeis(o)]();
}

É mais oneroso para digitar (ou ler) do que um comutador ou um objeto, mas preserva os benefícios do uso de um objeto em vez de um comutador, detalhados na seção de comentários abaixo. Esse estilo também torna mais fácil transformar isso em uma "classe" adequada, uma vez que cresce o suficiente.

update2: com extensões de sintaxe propostas para ES.next, isso se torna

let getInnerText = o -> ({
    string: o -> o,
    array: o -> o.map(getInnerText).join(""),
    object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);
bretão
fonte
3
É assim que o Python passa sem uma declaração de opção.
Outis
2
O problema é que sempre avalia todos os casos.
Kornel
@porneL isso é verdade, mas confere alguns benefícios: É logicamente mais limpo: os casos são cadeias que são pesquisadas em uma hashtable, não expressões que cada uma deve ser avaliada quanto à igualdade até que uma retorne verdadeira. Portanto, enquanto mais "valores" são avaliados, menos "chaves" são avaliadas. Os objetos podem ser gerados dinamicamente e modificados para posterior escalabilidade, refletidos na impressão da interface do usuário ou na geração de documentos e até mesmo substituídos por uma função dinâmica de "pesquisa", que é melhor do que ter casos de copiar / colar. Não há confusão sobre quebras, falhas ou valores padrão. Pode ser JSON serializado ...
Breton
@porneL oh sim, e novamente para a questão da escalabilidade, um objeto pode ser facilmente desdobrado em uma configuração externa ou arquivo de dados, uma mudança um pouco mais direta do que com uma instrução switch - mas trivial se projetado com um objeto em mente para começar com.
Breton
Eu sei que isso é uma entrada tardia, mas, a menos que você tenha alguma lógica de verificação de tipo personalizada, quando é que uma matriz funcionará com seu exemplo? var arr = []; typeof arr; // object
precisa saber é o seguinte
25

Certifique-se de usar o método hasOwnProperty ao iterar pelas propriedades de um objeto:

for (p in anObject) {
    if (anObject.hasOwnProperty(p)) {
        //Do stuff with p here
    }
}

Isso é feito para que você acesse apenas as propriedades diretas de anObject e não use as propriedades que estão na cadeia de protótipos.

Andreas Grech
fonte
23

Variáveis ​​privadas com uma interface pública

Ele usa um pequeno truque com uma definição de função de chamada automática. Tudo dentro do objeto retornado está disponível na interface pública, enquanto todo o resto é privado.

var test = function () {
    //private members
    var x = 1;
    var y = function () {
        return x * 2;
    };
    //public interface
    return {
        setx : function (newx) {
            x = newx;
        },
        gety : function () {
            return y();
        }
    }
}();

assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());
Chris MacDonald
fonte
1
isso é chamado de padrão de módulo, como foi chamado por Eric Miraglia em yuiblog.com/blog/2007/06/12/module-pattern Eu acho que o nome é enganoso, deveria ser chamado de Padrão Singleton ou algo assim. Também devo acrescentar que métodos públicos também podem chamar outros métodos públicos usando o objeto 'this'. Eu uso esse padrão o tempo todo no meu código para manter as coisas organizadas e limpas.
Mikeycgto 23/08/09