Funzionalità nascoste di JavaScript? [chiuso]


312

Quali "funzioni nascoste" di JavaScript pensi che ogni programmatore dovrebbe conoscere?

Dopo aver visto l'eccellente qualità delle risposte alle seguenti domande, ho pensato che fosse tempo di chiederlo per JavaScript.

Anche se JavaScript è probabilmente la lingua lato client più importante in questo momento (basta chiedere a Google) è sorprendente quanto poco la maggior parte degli sviluppatori web apprezzi quanto sia veramente potente.


1
Non volevi dire "Dopo aver visto i punti di riferimento e le opinioni attratte da questa altra domanda, ho pensato di fare quasi esattamente la stessa domanda per aumentare la mia"? ;-)
Bobby Jack

1
Certo, pessimista. :) Avevo pensato di fare di questo una domanda della comunità. Inoltre, dopo aver ottenuto un certo numero di punti, tutti i rendimenti diminuiscono.
Allain Lalonde,

1
Abbastanza giusto - non sembra che tu abbia "bisogno" del rappresentante! Immagino di avere un grosso problema con C # one - non mi sembra esattamente il tipo di domanda a cui questo sito era destinato.
Bobby Jack,

3
Sì, forse no, ma ho trovato grande la conoscenza nelle risposte. Penso che saresti difficile esporre un programmatore C # medio a tutto questo in un posto se non per SO. Ci vorrebbero anni a giocarci per trovare la stessa lista vinta.
Allain Lalonde

7
Scrivo JavaScript professionalmente da 10 anni e ho imparato una o tre cose da questa discussione. Grazie Alan!
Andrew Hedges,

Risposte:


373

Non è necessario definire alcun parametro per una funzione. Puoi semplicemente usare l' argumentsoggetto simile a matrice della funzione .

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

117
Vale la pena notare che sebbene gli argomenti si comportino come un array, non è un vero array javascript - è solo un oggetto. Quindi non puoi fare join (), pop (), push (), slice () e così via. (Puoi convertirlo in un vero array se vuoi: "var argArray = Array.prototype.slice.call (argomenti);")
Jacob Mattison,

51
Vale anche la pena notare che l'accesso all'oggetto Arguments è relativamente costoso - i migliori esempi sono nei nightly Safari, Firefox e Chrome, dove il semplice riferimento argumentsall'oggetto rende la chiamata di una funzione molto più lenta - ad es. if (false) argomenti; ferirà perf.
Olliej,

48
Allo stesso modo, gli argomenti hanno una proprietà "chiamata" che è la funzione corrente stessa. Ciò consente di fare ricorsioni con funzioni anonime, fantastico!
Vincent Robert,

4
@Nathan "f (x, y, z)" sembra migliore di "f ([x, y, z])".
Mark Cidade,

16
@ Vincent Robert: tieni presente che arguments.calleeè deprecato.
Ken,

204

Potrei citare la maggior parte dell'ottimo libro di Douglas Crockford JavaScript: The Good Parts .

Ma ne prenderò solo uno per te, usare sempre ===e !==invece di ==e!=

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

==non è transitivo. Se lo usi ===, darebbe falso per tutte queste affermazioni come previsto.


29
È un peccato che così tante persone pensino che Crockford sia onnisciente. Certo, il ragazzo è proprio sul punto con la maggior parte delle sue critiche, ma smetto di dare alla sua roba un sostegno generale come fanno tanti sviluppatori ...
Jason Bunting,

21
Secondo l'avvertimento di Jason. Il libro in sé è molto interessante e offre molti buoni consigli, ma DC è fin troppo convinto che il suo modo di fare le cose sia l'unico modo corretto, tutto il resto è "difettoso". Se desideri alcuni esempi, guarda le sue risposte sul gruppo Yahoo JSLint.
Zilk,

30
Usa === invece di == è un buon consiglio se sei confuso dalla digitazione dinamica e vuoi solo che sia "davvero" uguale. Quelli di noi che comprendono la digitazione dinamica possono continuare a utilizzare == per situazioni in cui sappiamo di voler trasmettere, come in 0 == '' o 0 == '0'.
thomasrutter,

20
Bene == e === non riguardano la digitazione dinamica. == digita coersion, che è una bestia diversa. Se sai che vuoi eseguire il cast su string / number / etc, allora devi farlo esplicitamente.
Rene Saarsoo,

15
Penso che la parte più spaventosa di ==sia '\n\t\r ' == 0=> true...: D
Shrikant Sharat

189

Le funzioni sono cittadini di prima classe in 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

Le tecniche di programmazione funzionale possono essere utilizzate per scrivere eleganti javascript .

In particolare, le funzioni possono essere passate come parametri, ad esempio Array.filter () accetta un callback:

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

Puoi anche dichiarare una funzione "privata" che esiste solo nell'ambito di una funzione specifica:

function PrintName() {
    var privateFunction = function() { return "Steve"; };
    return privateFunction();
}

3
Esistono tre modi per creare funzioni in javascript: funzione sum (x, y, z) {return (x + y + z); } e var sum = nuova funzione ("x", "y", "z", "return (x + y + z);"); sono gli altri modi.
Marius,

6
Il concetto di funzioni come dati guadagna sicuramente grandi punti nel mio libro.
Jason Bunting,

Ho appena aggiornato l'esempio per mostrare come utilizzare una funzione "privata" che esiste solo nell'ambito di una funzione specifica.
Chris Pietschmann,

new Function()è cattivo come eval. Non usare.
Nicolás,

11
non sono sicuro che questa sia una funzione nascosta ... più simile a una funzionalità di base.
Claudiu,

162

Puoi usare in all'operatore di controllare se una chiave esiste in un oggetto:

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 trovi gli oggetti letterali troppo brutti puoi combinarli con il suggerimento di funzione senza parametri:

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

22
Non così intelligente, che controlla se è presente una chiave, non se è presente un valore. x in elenco; funziona solo perché x [1]! = null, non perché il valore 1 è presente.
Armin Ronacher,

1
Non ho usato la tecnica per un po 'di tempo, quindi ho dimenticato di aver effettivamente usato oggetti letterali prima. Grazie per la correzione.
Mark Cidade,

34
Inoltre, fai attenzione: l'operatore in verifica anche la catena di prototipi! Se qualcuno ha inserito una proprietà chiamata '5' nell'Object.prototype, il secondo esempio restituirà true anche se si chiamasse '5 in list (1, 2, 3, 4)' ... Faresti meglio a usare hasOwnProperty metodo: list (1, 2, 3, 4) .hasOwnProperty (5) restituirà false, anche se Object.prototype ha una proprietà '5'.
Martijn,

3
Per la soluzione più generale, quella che può verificare se un oggetto ha una sua proprietà, anche se è chiamato "hasOwnProperty", devi andare fino in fondo: Object.prototype.hasOwnProperty.call (oggetto, nome) ;
Kris Kowal,

1
@Kris, a meno che qualcuno non sovrascriva Object.prototype.hasOwnProperty;)
Nick,

153

Assegnare valori predefiniti alle variabili

È possibile utilizzare la logica o l'operatore ||in un'espressione di assegnazione per fornire un valore predefinito:

var a = b || c;

La avariabile otterrà il valore csolo se bè falsy (se è null, false, undefined, 0, empty string, o NaN), altrimenti aotterrà il valore di b.

Ciò è spesso utile nelle funzioni, quando si desidera dare un valore predefinito a un argomento nel caso in cui non venga fornito:

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

Esempio di fallback di IE nei gestori di eventi:

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

Le seguenti funzionalità linguistiche sono state con noi per molto tempo, tutte le implementazioni JavaScript le supportano, ma non facevano parte delle specifiche fino alla quinta edizione di ECMAScript :

La debuggerdichiarazione

Descritto in: § 12.15 L'istruzione debugger

Questa affermazione ti consente di inserire punti di interruzione a livello di codice nel tuo codice semplicemente:

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

Se un debugger è presente o attivo, si interromperà immediatamente, proprio su quella linea.

Altrimenti, se il debugger non è presente o attivo questa affermazione non ha alcun effetto osservabile.

Letterali stringa multilinea

Descritto in: § 7.8.4 Letterali a stringa

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

Bisogna stare attenti perché il carattere successivo al \ deve essere un terminatore di linea, se si dispone di uno spazio dopo il \per esempio, il codice sarà guardare esattamente lo stesso, ma sarà sollevare una SyntaxError.


28
Non se è nullo, se è considerato falso. a = 0 || 42; ti darà 42. Questo è paragonabile a Python o, non a C # ?? operatore. Se si desidera il comportamento C #, eseguire a = (b === null)? c: b;
Armin Ronacher,

Funziona anche in Visual Studio, se sviluppi su ASP.NET :)
chakrit

2
Vorrei che ci fosse proprio || solo per indefinito. Sono stato morso da questo oggi per 0, poiché volevo creare l'emulazione del metodo sovraccarico, in modo che l'ultimo argomento fosse facoltativo e al suo posto sarebbe stato usato un valore predefinito.
egaga,

+1 questo trucco è utilizzato dallo snippet predefinito di Google Analytics. `var _gaq = _gaq || []; `; impedisce agli utenti troppo zelanti di sovrascrivere il proprio lavoro.
Yahel,

2
Non sapevo della tecnica letterale di stringa multilinea. È fantastico, grazie.
Charlie Flowers,

145

JavaScript non ha un ambito di blocco (ma ha la chiusura, quindi chiamiamolo anche?).

var x = 1;
{
   var x = 2;
}
alert(x); // outputs 2

3
Questa è buona. È una differenza davvero importante dalla maggior parte delle lingue simili al C.
Martin Clarke,

9
Puoi sempre fare "var tmp = function () {/ * block scope * /} ();". La sintassi è brutta, ma funziona.
Joeri Sebrechts,

3
Oppure puoi usare "let" se è solo Firefox: stackoverflow.com/questions/61088/…
Eugene Yokota,

10
o semplicemente: (function () {var x = 2;}) (); avviso (tipo di x); // undefined
Pim Jager,

@Pim: JSLint dice: "Sposta l'invocazione nelle parentesi che contengono la funzione.". Insieme a "Previsto esattamente uno spazio tra 'funzione' e '('.".
Ciao,

144

È possibile accedere alle proprietà dell'oggetto con []anziché.

Ciò consente di cercare una proprietà corrispondente a una variabile.

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

Puoi anche usarlo per ottenere / impostare proprietà dell'oggetto il cui nome non è un identificatore legale.

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.

Alcune persone non lo sanno e finiscono per usare eval () in questo modo, che è una pessima idea :

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

È più difficile da leggere, più difficile da trovare errori (impossibile usare jslint), più lento da eseguire e può portare a exploit XSS.


eval è malvagio, anche se raramente necessario
Doug Domeny,

Non uso mai eval e ricordo quando l'ho scoperto. Mi ha fatto molto piacere.

In sintesi, è possibile accedere alle proprietà degli oggetti tramite la notazione sia con punti che con sottoscrizioni
Russ Cam

9
È interessante notare che il riferimento a punti è in realtà zucchero di sintassi per il bracketref. foo.bar, secondo le specifiche comunque, si comporta proprio come foo["bar"]. nota inoltre che tutto è una proprietà stringa. anche quando si accede alla matrice array[4], il 4 viene convertito in una stringa (di nuovo, almeno secondo le specifiche ECMAScript v3)
Claudiu

Immagino che ogni programmatore JS dovrebbe saperlo.
Cem Kalyoncu,

144

Se stai cercando su Google un riferimento JavaScript decente su un determinato argomento, includi la parola chiave "mdc" nella tua query e i tuoi primi risultati verranno dal Mozilla Developer Center. Non porto con me riferimenti o libri offline. Uso sempre il trucco delle parole chiave "mdc" per arrivare direttamente a quello che sto cercando. Per esempio:

Google: array javascript ordina mdc
(nella maggior parte dei casi potresti omettere "javascript")

Aggiornamento: Mozilla Developer Center è stato rinominato in Mozilla Developer Network . Il trucco della parola chiave "mdc" funziona ancora, ma presto potremmo dover iniziare a usare "mdn" .


50
Caspita, grande risorsa. Immediatamente meglio delle schifose w3schools ...
DisgruntledGoat

11
Non hai nemmeno bisogno di cercarlo su Google, se sei su Firefox: digita "array mdc" nella barra degli indirizzi e premi Invio.
Sasha Chedygov,

2
la parte migliore è come questa domanda di overflow dello stack si trova nella prima pagina dei risultati :)
Jiaaro,

5
A propo questo: promojs.com , un'iniziativa SEO di base per aumentare ulteriormente i risultati MDC nei risultati di ricerca di Google.
Yahel,

3
Ora è il doc center MDN, quindi la parola chiave 'mdc' è ancora valida :)
Aleadam

143

Forse un po 'ovvio per alcuni ...

Installa Firebug e usa console.log ("ciao"). Molto meglio dell'utilizzo di random alert (); 'che ricordo di aver fatto molto qualche anno fa.


12
Non dimenticare di rimuovere le istruzioni della console prima di rilasciare il codice ad altri che potrebbero non aver installato Firebug.
Chris Noe,

161
log delle funzioni (msg) {if (console) console.log (msg) else alert (msg)}
Josh,

4
Ancora meglio, precede le istruzioni di registro con ';;;' e quindi minify se ne prende cura per te. (Almeno, il modulo Perl che uso ha quella funzione e afferma che è un luogo comune.)
Kev

10
Josh: Non funzionerà perché la console non è definita. Puoi controllare typeof console! == "undefined" o window.console.
Eli Gray,

23
Includi sempre: if (typeof ('console') == 'undefined') {console = {log: function () {}}; } allora puoi continuare a usare console.log e non fa nulla.
Gregreg

120

Metodi privati

Un oggetto può avere metodi privati.

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());

16
Non è proprio una funzione privata: è più una variabile di funzione in un ambito locale.
Keith,

6
Vero, ma secondo tutte le definizioni operative posso pensare che sia un metodo. È un blocco di codice con un nome che ha accesso allo stato dell'istanza e può essere visto solo da quell'istanza. Qual è la tua definizione di metodo privato?
Allain Lalonde,

14
@Zach, esattamente! È facile, dopo aver trascorso anni a lavorare con linguaggi OO di classe, dimenticare che si tratta semplicemente di un'implementazione dei concetti di OO. Ovviamente, le varie librerie che tentano di stipare OO basato su quasi classi in JS non aiutano neanche ...
Shog9

5
Mi chiedo, person1 ha un Blog sulla legge? ;-)
travis,

4
+1 per il riferimento allo sviluppo arrestato
Domenic,

99

Anche menzionato nel "Javascript: The Good Parts" di Crockford:

parseInt()è pericoloso. Se gli passi una stringa senza informarla della base corretta, potrebbe restituire numeri imprevisti. Ad esempio parseInt('010')restituisce 8, non 10. Passare una base a parseInt fa funzionare correttamente:

parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.

13
Quando si eseguono revisioni del codice, cercare sempre questo. Lasciare ", 10" è un errore comune che passa inosservato nella maggior parte dei test.
Doug Domeny,

Sono stato bruciato dal problema di radix anni fa e non ho mai dimenticato qualcosa di così contro-intuitivo in quanto tale. Una grande cosa da sottolineare dal momento che ti farà meravigliare per un po '.
JamesEggers,

4
Perché non usare Math.flooro Number? 10 === Math.floor("010"); 10 === Number("010");galleggia:42 === Math.floor("42.69"); 42.69 === Number("42.69");
solo qualcuno il

1
@Infinity Se non hai già una risposta postata, dovresti. Non avevo idea che fosse così semplice ignorare il comportamento delle funzioni integrate. Ovviamente, si dovrebbe dare uno sguardo un po 'più da vicino a tutti i pacchetti di codice che prendono in prestito da altri siti. Quella innocua parseIntfunzione potrebbe facilmente essere fatta per fare qualcosa di non così innocuo.
bob-the-destroyer,

6
@Infinity: che ne dici di ridefinire l'fn per evidenziare "l'errore di codifica"? __parseInt = parseInt; parseInt = function (str, base) { if (!base) throw new Error(69, "All your base belong to us"); return __parseInt(str, base); }
JBR Wilkinson,

97

Le funzioni sono oggetti e quindi possono avere proprietà.

fn = funzione (x) {
   // ...
}

fn.foo = 1;

fn.next = function (y) {
  //
}

13
Questo è un consiglio molto utile. Ad esempio, è possibile impostare i valori predefiniti come proprietà della funzione. Ad esempio: myfunc.delay = 100; Quindi gli utenti possono modificare il valore predefinito e tutte le chiamate di funzione utilizzeranno il nuovo valore predefinito. Ad esempio: myfunc.delay = 200; myfunc ();
BarelyFitz,

Utile ... e pericoloso!
palswim,

Sembra sciatto, perché usare questo invece di una variabile?
Instantsetsuna

1
@instantsetsuna: perché avere un'altra variabile separata ? Come al solito, si riduce a "usarlo quando appropriato / utile" ;-)
VolkerK

91

Dovrei dire funzioni di auto-esecuzione.

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

Poiché Javascript non ha un ambito di blocco , è possibile utilizzare una funzione di auto-esecuzione se si desidera definire le variabili locali:

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

Qui, myvaris non interferisce con o inquina l'ambito globale e scompare quando termina la funzione.


2
A cosa serve questo? Si ottengono gli stessi risultati mettendo l'avviso fuori dalla funzione.
PotatoEngineer,

7
Non si tratta dell'avviso, si tratta di definire ed eseguire una funzione tutto in una volta. È possibile che quella funzione auto-eseguente restituisca un valore e passi la funzione come parametro a un'altra funzione.
ScottKoon

5
@Paul è buono per l'incapsulamento.
Mike Robinson

22
È anche buono per l'ambito scoping.
Jim Hunziker,

24
Sì, allego tutti i miei .jsfile in una funzione di auto-esecuzione anonima e allego tutto ciò che voglio accessibile globalmente all'interno windowdell'oggetto. Previene l'inquinamento dello spazio dei nomi globale.
cdmckay,

83

Sapere quanti parametri sono previsti da una funzione

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

Sapere quanti parametri sono ricevuti dalla funzione

function add_many_nums(){
    return arguments.length;
}    
add_many_nums(2,1,122,12,21,89); //returns 6

23
Non ho mai saputo della prima parte. Bello!
McJabberz,

1
Allo stesso modo puoi scoprire con quanti argomenti si aspetta una funzione function.length.
Xavi,

6
@Xavi che è la prima parte della risposta
pramodc84

79

Ecco alcune cose interessanti:

  • Confrontando NaNcon qualsiasi cosa (anche NaN) è sempre false, che comprende ==, <e> .
  • NaN Sta per Not a Number ma se chiedi il tipo in realtà restituisce un numero.
  • Array.sort può assumere una funzione di confronto e viene chiamato da un driver simile a quicksort (dipende dall'implementazione).
  • Le "costanti" di espressioni regolari possono mantenere lo stato, come l'ultima cosa a cui corrispondono.
  • Alcune versioni di JavaScript consentono di accedere $0, $1, $2i membri su una regex.
  • nullè diverso da qualsiasi altra cosa. Non è né un oggetto, un booleano, un numero, una stringa, né undefined. È un po 'come un "alternato" undefined. (Nota:typeof null == "object" :)
  • Nel contesto più esterno, this produce l'oggetto [Globale] altrimenti non modificabile.
  • Dichiarare una variabile con var , invece di basarsi semplicemente sulla dichiarazione automatica della variabile offre al runtime una reale possibilità di ottimizzare l'accesso a quella variabile
  • Il with costrutto distruggerà tali ottimizzazioni
  • I nomi delle variabili possono contenere caratteri Unicode.
  • Le espressioni regolari JavaScript non sono effettivamente regolari. Sono basati sulle regex di Perl ed è possibile costruire espressioni con lookahead che impiegano molto, molto tempo per essere valutate.
  • I blocchi possono essere etichettati e utilizzati come target di break. I loop possono essere etichettati e utilizzati come target di continue.
  • Le matrici non sono sparse. L'impostazione del 1000 ° elemento di un array altrimenti vuoto dovrebbe riempirlo di undefined. (dipende dall'implementazione)
  • if (new Boolean(false)) {...} eseguirà il {...}blocco
  • I motori di espressione regolare di Javascript sono specifici dell'implementazione: ad es. È possibile scrivere espressioni regolari "non portatili".

[aggiornato un po 'in risposta a buoni commenti; si prega di vedere i commenti]


5
null è in realtà un oggetto (speciale). typeof nullrestituisce "oggetto".
Ates Goral,

4
Puoi anche ottenere l'oggetto [Global] da qualsiasi posto come questo: var glb = function () {return this; } ();
Zilk,

2
L'oggetto globale in JavaScript in un browser è l'oggetto finestra. Quando si fa nell'ambito globale: window.a == a;
Pim Jager,

8
"Le matrici non sono sparse" dipende dall'implementazione. Se imposti il ​​valore di un [1000] e osservi un [999], allora sì, lo è undefined, ma questo è solo il valore predefinito che ottieni quando cerchi un indice che non esiste. Se hai controllato un [2000], sarebbe anche quello undefined, ma ciò non significa che hai ancora allocato memoria per esso. In IE8, alcuni array sono densi e altri sono scarsi, a seconda di come si sentiva il motore JScript in quel momento. Maggiori informazioni qui: blogs.msdn.com/jscript/archive/2008/04/08/…
Chris Nielsen,

2
@Ates e @SF: typeof restituisce "object" per una gamma di tipi diversi. Ma una volta che sai come funziona e quali tipi si identificano come "oggetto", è almeno affidabile e coerente nella sua implementazione.
thomasrutter,

77

So di essere in ritardo alla festa, ma non riesco proprio a credere +che l'utilità dell'operatore non sia stata menzionata oltre a "convertire qualsiasi cosa in un numero". Forse è così ben nascosta una caratteristica?

// 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

Ovviamente, puoi fare tutto questo usando Number()invece, ma il+ operatore è molto più carino!

È inoltre possibile definire un valore di ritorno numerico per un oggetto sovrascrivendo il valueOf()metodo del prototipo . Qualsiasi conversione numerica eseguita su quell'oggetto non comporterà NaN, ma il valore di ritorno del valueOf()metodo:

var rnd = {
    "valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd;               // -> 442;
+rnd;               // -> 727;
+rnd;               // -> 718;

Puoi fare semplicemente 0xFFecc., Non è necessario +"0xFF".
nyuszika7h

9
@ Nyuszika7H: ti manca un po 'il punto, che sta costringendo altri primitivi e oggetti ai numeri. Naturalmente si può solo scrivere 0xFF, più o meno allo stesso modo in cui è possibile scrivere 1al posto di +true. Sto suggerendo che puoi usare +("0x"+somevar)come alternativa a parseInt(somevar, 16), se lo desideri.
Andy E

75

" Metodi di estensione in JavaScript " tramite la proprietà prototype.

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

Ciò aggiungerà un containsmetodo a tutti gli Arrayoggetti. È possibile chiamare questo metodo utilizzando questa sintassi

var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");

18
Questa è generalmente considerata una cattiva idea, perché altri codici (non i tuoi) possono fare ipotesi sull'oggetto Array.
Chris Noe,

39
Inoltre è generalmente considerata una cattiva idea fare ipotesi sull'oggetto Array. :(
eyelidlessness

Uhmmmm .. extra di array javascript 1.6? indice di? suonare delle campane?
Bretone,

2
@Breton: non è qualcosa di specifico per la classe Array, è solo un esempio. Lo uso per estendere la nuova Data (). AString (); metodo, che consente di utilizzare una stringa di maschera. Qualsiasi oggetto può essere esteso e tutte le sue istanze ottengono il nuovo metodo.
Esteban Küber,

1
@Mathias: non si tratta del DOM.
dolmen,

60

Per rimuovere correttamente una proprietà da un oggetto, è necessario eliminare la proprietà anziché impostarla su non definita :

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

obj.prop2 = undefined;

for (var key in obj) {
    ...

La proprietà prop2 farà ancora parte dell'iterazione. Se vuoi sbarazzarti completamente di prop2 , dovresti invece fare:

delete obj.prop2;

La proprietà prop2 non apparirà più quando stai iterando attraverso le proprietà.


3
Si noti che l'istruzione delete non è priva di stranezze specifiche del browser. Ad esempio questo fallirà con un grosso errore se lo provi in ​​IE e l'oggetto non è un oggetto JS nativo (anche quando elimini una proprietà che hai aggiunto tu stesso). Inoltre, non è destinato all'eliminazione di una variabile, come in delete myvar; ma penso che funzioni in alcuni browser. Il codice nella risposta sopra sembra abbastanza sicuro però.
thomasrutter,

a proposito, anche undefined può essere una variabile! Prova var undefined = "qualcosa"
Johann Philipp Strathausen,

57

with.

È usato raramente e francamente, raramente utile ... Ma, in circostanze limitate, ha i suoi usi.

Ad esempio: i letterali di oggetti sono molto utili per impostare rapidamente le proprietà su un nuovo oggetto. E se fosse necessario modificare la metà delle proprietà su un oggetto esistente?

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

// ...

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

Alan Storm sottolinea che questo può essere alquanto pericoloso: se l'oggetto utilizzato come contesto non ha una delle proprietà assegnate, verrà risolto nell'ambito esterno, creando o sovrascrivendo una variabile globale. Ciò è particolarmente pericoloso se sei abituato a scrivere codice per lavorare con oggetti in cui le proprietà con valori predefiniti o vuoti non vengono definite:

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

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

Pertanto, è probabilmente una buona idea evitare l'uso withdell'istruzione per tale assegnazione.

Vedi anche: ci sono usi legittimi per l'affermazione "con" di JavaScript?


29
La saggezza convenzionale deve essere evitata. Se l'oggetto utente non avesse una delle proprietà menzionate, la variabile esterna allo pseudo-ambito del blocco with verrebbe modificata. In questo modo si trovano bug. Maggiori informazioni su yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Alan Storm

1
Shog, le obiezioni non riguardano le variabili errate, si tratta di guardare un blocco di codice e di essere in grado di dire con certezza cosa fa una particolare riga in quel blocco. Poiché gli oggetti Javascript sono così dinamici, non si può dire con certezza quali proprietà / membri abbia in qualsiasi momento.
Alan Storm,

2
Amen - se vedessi la frase "with" in qualsiasi JS che trovassi, lo eliminerei e metterei in discussione lo sviluppatore che lo ha scritto per assicurarsi che sapesse perché non era una buona cosa usarlo ... "funzionalità nascosta?" Più come "caratteristica disgustosa".
Jason Bunting,

1
considera una catena più complessa abcd "con (abc) {d.foo = bar;} è potente e non intrinsecamente soggetto a errori. La chiave è ridurre la radice di un livello. E scrivere male un nome di variabile? Stai introducendo un bug se lo fai ovunque lo fai, indipendentemente da "con".
annakata,

4
Douglas Crockford ha recentemente affermato che "with" è una delle parti peggiori di JavaScript in un .NET Rocks! podcast.
core

51

I metodi (o le funzioni) possono essere richiamati su oggetti che non sono del tipo con cui sono stati progettati per funzionare. È fantastico chiamare metodi nativi (veloci) su oggetti personalizzati.

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

Questo codice si arresta in modo anomalo perché listNodesnon è unArray

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

Questo codice funziona perché listNodesdefinisce abbastanza proprietà simili a array (lunghezza, operatore []) per essere utilizzate da sort().


43

L'eredità prototipale (resa popolare da Douglas Crockford) rivoluziona completamente il modo in cui pensi a un sacco di cose in Javascript.

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

È un assassino! Peccato che quasi nessuno lo usi.

Ti consente di "generare" nuove istanze di qualsiasi oggetto, di estenderle, mantenendo al contempo un collegamento di eredità prototipo (attivo) alle loro altre proprietà. Esempio:

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'

42

Alcuni lo definirebbero una questione di gusti, ma:

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

L'operatore trinario può essere incatenato per agire come quello di Scheme (cond ...):

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

può essere scritto come ...

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

Questo è molto "funzionale", in quanto ramifica il tuo codice senza effetti collaterali. Quindi invece di:

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

Tu puoi scrivere:

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

Funziona bene anche con la ricorsione :)


Mi piace la sintassi del predicato che dai. Non ho mai pensato di concatenarmi in quel modo. pulito.
Allain Lalonde,

2
Uh ... JavaScript ha un'istruzione switch (). :-)
staticsan

Non sono un grande fan delle dichiarazioni switch - sono un artefatto di C, non una programmazione funzionale. Nel mio esempio, un'istruzione switch avrebbe ancora bisogno di tre istruzioni separate, tutte che iniziano con "foo =" - ovvia ripetizione non necessaria.
Andrey Fedorov,

14
Per uno, accolgo con favore l'operatore ternario.
thomasrutter,

8
Rileggendo, vorrei sottolineare che questo non "fa sembrare il codice come un'altra lingua", ma semplifica il significato semantico del codice: quando stai cercando di dire "imposta foo su uno dei tre cose ", questa è un'affermazione che dovrebbe iniziare con" pippo = ... ", non" se ".
Andrey Fedorov,

41

Anche i numeri sono oggetti. Quindi puoi fare cose interessanti come:

// 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

OH MIO DIO! Non sapevo di ToString (radix) ...
Ates Goral

1
Tale implementazione di timesnon è efficiente: Math.floorviene chiamata ogni volta anziché solo una volta.
dolmen,

33

Che ne dici di chiusure in JavaScript (simili ai metodi anonimi in C # v2.0 +). È possibile creare una funzione che crea una funzione o "espressione".

Esempio di chiusure :

//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);

1
non sono sicuro, ma posso restituire (numberToCheck> lowerBound)? vero falso; diventa semplicemente return (numberToCheck> lowerBound); sto solo cercando di aumentare la mia comprensione ...
davidsleeps,

4
Direi che le funzioni anonime in C # sono equivalenti a chiusure, non viceversa :)
vava,

11
Le chiusure e le funzioni anonime sono concetti separati e distinti. Quelle funzioni che possono essere create senza essere nominate hanno funzioni anonime. Che una variabile nell'ambito di "creazione" sia collegata alla funzione creata è una chiusura. In breve, una chiusura è più simile a una variabile globale nascosta.
slebetman,

1
È vero. Solo quando i metodi anonimi utilizzano una variabile dall'ambito di creazione è simile a una chiusura. Ho aggiornato l'inglese sulla risposta. Lascia ancora qualcosa a desiderare, ma sono perso per l'inglese corretto.
Tyler,

2
Non penso che questo sia l'esempio migliore o più facile da capire di cosa sia una chiusura. Sto solo dicendo. Il punto di chiusura è che anche quando un gruppo di variabili sembra "uscire dall'ambito di applicazione", possono comunque rimanere disponibili per una funzione che era stata originariamente definita all'interno di tale ambito. Nell'esempio sopra, ciò significa che la variabile lowerBound è ancora accessibile da quella funzione interna, anonima, anche quando termina la funzione esterna, buildGreaterThanFunction.
thomasrutter,

32

È inoltre possibile estendere (ereditare) le classi e sovrascrivere le proprietà / i metodi utilizzando il prototipo di catena cucchiaio16 cui si fa riferimento.

Nel seguente esempio creiamo una classe Pet e definiamo alcune proprietà. Sostituiamo anche il metodo .toString () ereditato da Object.

Successivamente creiamo una classe Dog che estende Pet e sovrascrive nuovamente il metodo .toString () modificandone il comportamento (polimorfismo). Inoltre aggiungiamo alcune altre proprietà alla classe figlio.

Dopodiché controlliamo la catena di ereditarietà per mostrare che Dog è ancora di tipo Dog, di tipo Pet e di 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

Entrambe le risposte a questa domanda sono state modificate da un ottimo articolo MSDN di Ray Djajadinata.


31

È possibile rilevare eccezioni a seconda del tipo. Citato da 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: le clausole di cattura condizionale sono un'estensione Netscape (e quindi Mozilla / Firefox) che non fa parte delle specifiche ECMAScript e quindi non può essere invocata se non su determinati browser.


29
Non ho potuto farne a meno: cattura (me se puoi)
Ates Goral,

6
Leggi la nota dalla pagina MDC che hai citato: le clausole catch condizionali sono un'estensione Netscape (e quindi Mozilla / Firefox) che non fa parte delle specifiche ECMAScript e quindi non può essere invocata se non su particolari browser.
Jason S,

31

Dalla cima della mia testa ...

funzioni

argument.callee si riferisce alla funzione che ospita la variabile "argomenti", quindi può essere utilizzata per richiamare funzioni anonime:

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

È utile se vuoi fare qualcosa del genere:

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

Oggetti

Una cosa interessante sui membri dell'oggetto: possono avere qualsiasi stringa come i loro nomi:

//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);
}

stringhe

String.split può assumere espressioni regolari come parametri:

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

String.replace può assumere un'espressione regolare come parametro di ricerca e una funzione come parametro sostitutivo:

var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"

Le cose che dici ... Sono implementate in tutti i browser?
cllpse,

4
No. Sono abbastanza sicuro che a Mosaic manchi la maggior parte di essi.
jsight,

2
Le funzionalità javascript, sì, sono implementate in tutti i principali browser (IE6 / 7, FF2 / 3, Opera 9+, Safari2 / 3 e Chrome). document.querySelectorAll non è ancora supportato in tutti i browser (è la versione W3C di $ () di JQuery e $$ () di Prototype
Leo

6
arguments.calleeè obsoleto e genererà un'eccezione in ECMAScript 5.
Hello71,

non del tutto vero. Una chiave oggetto non può (o meglio, non dovrebbe) usare la stringa "hasOwnProperty" come nome, in quanto sovrascriverebbe il metodo dell'oggetto incorporato.
Breton

29

È possibile utilizzare gli oggetti anziché gli switch per la maggior parte del tempo.

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

Aggiornamento: se sei preoccupato che i casi che valutano in anticipo siano inefficienti (perché sei preoccupato per l'efficienza fin dall'inizio nella progettazione del programma ??) allora puoi fare qualcosa del genere:

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)]();
}

Questo è più oneroso da digitare (o leggere) rispetto a uno switch o a un oggetto, ma conserva i vantaggi dell'utilizzo di un oggetto anziché di uno switch, dettagliato nella sezione commenti qui sotto. Questo stile rende anche più semplice trasformarlo in una vera "classe" una volta cresciuto abbastanza.

update2: con le estensioni di sintassi proposte per ES.next, questo diventa

let getInnerText = o -> ({
    string: o -> o,
    array: o -> o.map(getInnerText).join(""),
    object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);

3
È così che Python riesce senza un'istruzione switch.
uscita il

2
Il problema è che valuta sempre tutti i casi.
Kornel,

@porneL questo è vero, ma conferisce alcuni vantaggi: è logicamente più pulito: i casi sono stringhe che vengono cercate in una tabella hash, non espressioni che devono essere valutate per l'uguaglianza fino a quando non si ritorna vero. Pertanto, mentre vengono valutati più "valori", vengono valutati meno "chiavi". Gli oggetti possono essere generati dinamicamente e modificati per una successiva scalabilità, riflessi per la stampa dell'interfaccia utente o la generazione di documenti e persino sostituiti con una funzione di "ricerca" dinamica, che è meglio che avere casi di copia / incolla. Non c'è confusione su interruzioni, fall-through o valori predefiniti. Può essere serializzato JSON ...
Breton,

@porneL oh sì, e di nuovo per quanto riguarda la scalabilità, un oggetto può anche essere facilmente trasformato in una configurazione esterna o in un file di dati, un cambiamento un po 'più semplice rispetto a un'istruzione switch - Ma banale se progettato con un oggetto in mente per iniziare con.
Breton,

so che questa è una voce in ritardo, ma a meno che tu non abbia una logica di controllo del tipo personalizzata, quando un array funzionerà mai con il tuo esempio? var arr = []; typeof arr; // object
keeganwatkins,

25

Assicurati di utilizzare il metodo hasOwnProperty durante l'iterazione delle proprietà di un oggetto:

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

Questo viene fatto in modo da accedere solo alle proprietà dirette di anObject e non utilizzare le proprietà che si trovano nella catena del prototipo.


23

Variabili private con un'interfaccia pubblica

Usa un piccolo trucco con una definizione della funzione di auto-chiamata. Tutto all'interno dell'oggetto che viene restituito è disponibile nell'interfaccia pubblica, mentre tutto il resto è privato.

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());

1
questo è chiamato il pattern module, come è stato soprannominato da Eric Miraglia su yuiblog.com/blog/2007/06/12/module-pattern Penso che il nome sia fuorviante, dovrebbe essere chiamato Singleton Pattern o qualcosa del genere. Potrei anche aggiungere che i metodi pubblici possono anche chiamare altri metodi pubblici usando 'questo' oggetto. Uso questo modello continuamente nel mio codice per mantenere le cose organizzate e pulite.
mikeycgto,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.