2012-03-17 12 views
25

sono incappato in quel test delle prestazioni, dicendo che espressioni regolari in JavaScript non sono necessariamente rallentare: http://jsperf.com/regexp-indexof-perfdinamica vs prestazioni Inline RegExp in JavaScript

C'è una cosa che non ho avuto però: due casi riguardano qualcosa che credevo di esattamente la stessa cosa:

RegExp('(?:^|)foo(?: |$)').test(node.className); 

E

/(?:^|)foo(?: |$)/.test(node.className); 

Nella mia mente, queste due linee sono state esattamente lo stesso, il secondo è una sorta di abbreviazione per creare un oggetto RegExp. Ancora, è due volte più veloce rispetto al primo.

Questi casi sono denominati "regexp dinamico" e "regexp in linea".

Qualcuno potrebbe aiutarmi a capire la differenza (e il divario di prestazioni) tra questi due?

+1

È positivo che la versione "in linea" sia più veloce, poiché è molto meno brutta rispetto all'utilizzo del costruttore esplicito. – Pointy

+0

Per esempio, potresti aver sovrascritto 'RegExp' in modo che a) debba cercare la funzione invece di valutarla direttamente, e b) la seconda possa essere valutata in fase di analisi mentre la prima non può perché la chiamata di' RegExp' può avere effetti collaterali nel caso in cui l'hai sovrascritto. – pimvdb

risposta

8

La differenza di prestazioni non è correlata alla sintassi utilizzata.

Per inlineRegExp e storedRegExp si sta guardando codice che viene inizializzato una volta che quando il testo del codice sorgente viene analizzato, mentre per dynamicRegExp si crea l'espressione regolare per ogni invocazione del metodo. Si noti che il actual tests esegue cose come r = dynamicRegExp(element) molte volte, mentre il codice di preparazione viene eseguito una sola volta.

Di seguito ti dà gli stessi risultati, according to another jsPerf:

var reContains = /(?:^|)foo(?: |$)/; 

... e

var reContains = RegExp('(?:^|)foo(?: |$)'); 

... quando entrambi sono utilizzati con

function storedRegExp(node) { 
    return reContains.test(node.className); 
} 

Certo, il codice sorgente di RegExp('(?:^|)foo(?: |$)') potrebbe essere analizzato in un String, un Poi in un RegExp, ma dubito che di per sé sarà due volte più lento. Tuttavia, il seguente sarà creare un nuovo RegExp(..)ancora e ancora per ogni chiamata di metodo:

function dynamicRegExp(node) { 
    return RegExp('(?:^|)foo(?: |$)').test(node.className); 
} 

Se nella prova iniziale si sarebbe solo chiamare ogni metodo, una volta, quindi la versione in linea sarebbe non essere un enorme 2 tempi più veloci.

(io sono più sorpreso che inlineRegExp e storedRegExp hanno risultati diversi.)

+1

Questo in realtà ha molto senso, grazie. Potresti voler evidenziare [il tuo jsperf] (http://jsperf.com/regexp-indexof-perf/24) poiché mi ha aiutato a capire da dove viene la differenza di prestazioni (tra 'dynamicStoredRegExp' e' dynamicRegExp'). E a proposito della tua ultima affermazione, non è una gran differenza, lo ignorerei. – Pioul

+0

Per quanto riguarda la differenza tra 'inlineRegExp' e' storedRegExp': il primo [è la metà più veloce nell'ultimo Safari 6.0] (http://jsperf.com/regexp-indexof-perf/24) ...‽ – Arjan

+1

Non ho visto che, su Safari 6.0 sia 'storedRegExp' che' dynamicStoredRegExp' sono circa il doppio della 'inlineRegExp', quando su altri browser è praticamente lo stesso. Ora sono anche curioso di sapere cosa potrebbe accadere qui con Safari ... – Pioul

6

nel secondo caso, l'oggetto espressione regolare viene creato durante l'analisi della lingua e, nel primo caso, il costruttore della classe RegExp deve analizzare una stringa arbitraria.

+0

Quello che stai dicendo è che nel primo caso, il tipo regexp passa attraverso uno stato "stringa" prima di essere "compreso" dal motore? – Pioul

+0

sì, è vero. – dldnh

+0

oh, ancora meglio, dimmi se ho ragione: le barre funzionano come delimitatori regex, così che una barra implica una regex tanto quanto una citazione implica una stringa? – Pioul

13

Al giorno d'oggi, le risposte qui riportati non sono del tutto complete/corretta.

A partire dal ES5, il comportamento della sintassi letterale è lo stesso di creazione per quanto riguarda oggetto RegExp() sintassi: entrambi crea un nuovo oggetto RegExp ogni volta percorso di codice colpisce un'espressione in cui si stanno prendendo parte.

Pertanto, l'unica differenza tra i due ora è come capita spesso che regexp viene compilato:

  • Con la sintassi letterale - una volta durante l'analisi di codice iniziale e compilazione
  • Con RegExp() sintassi - ogni volta viene creato un nuovo oggetto

Si veda, ad esempio, Stoyan Stefanov's JavaScript Patterns libro:

Un'altra distinzione tra l'espressione regolare letterale e il costruttore è che il letterale crea un oggetto solo una volta durante fase di analisi. Se crei la stessa espressione regolare in un ciclo, l'oggetto creato in precedenza verrà restituito con tutte le sue proprietà (come lastIndex) già impostate dalla prima volta. Si consideri l'esempio seguente come un'illustrazione di come lo stesso oggetto è restituito due volte.

function getRE() { 
    var re = /[a-z]/; 
    re.foo = "bar"; 
    return re; 
} 

var reg = getRE(), 
    re2 = getRE(); 

console.log(reg === re2); // true 
reg.foo = "baz"; 
console.log(re2.foo); // "baz" 

Questo comportamento è cambiato nel ES5 e letterale crea anche nuovi oggetti. Il comportamento è stato corretto anche in molti ambienti del browser , quindi non è possibile fare affidamento su di esso.

Se si esegue questo esempio in tutti i browser moderni o NodeJS, si ottiene la seguente invece:

false 
bar 

Il che significa che e molto tempo si sta chiamando la funzione getRE(), un nuovo oggetto RegExp viene creato anche con l'approccio di sintassi letterale.

L'spiega sopra, non solo perché non si dovrebbe usare la RegExp() per espressioni regolari immutabili (è ben noto problema di prestazioni di oggi), ma spiega anche:

(io sono più sorpreso che inlineRegExp e storedRegExp hanno diversi risultati)

Il storedRegExp è di circa 5 - 20%. cento più veloce di tutti i browser inlineRegExp perché non c'è sovraccarico di creare (e di raccolta dei rifiuti) una nuova vigilia RegExp oggetto ry time.

Conclusione:
creare sempre le espressioni regolari immutabili con la sintassi letterale e la cache è se è di essere ri-usato.In altre parole, non fare affidamento su questa differenza di comportamento in envs al di sotto di ES5 e continuare a eseguire il caching in modo appropriato in envs sopra.

Perché sintassi letterale? Essa ha alcuni vantaggi rispetto alla sintassi costruttore:

  1. è più breve e non ti costringono a pensare in termini di classe simile costruttori.
  2. Quando si utilizza il costruttore RegExp(), è inoltre necessario evitare le virgolette e le barre di escape doppie. Rende le espressioni regolari difficili da leggere e comprendere per loro natura ancora più difficile.

(citazione libera dallo stesso Stoyan Stefanov's JavaScript Patterns libro).
Quindi, è sempre una buona idea attenersi alla sintassi letterale, a meno che l'espressione regolare non sia nota al momento della compilazione.

+0

Grazie per questo bel aggiornamento! Mi piace anche la conclusione, anche se sarei tentato di dire "vai con quello che preferisci per creare un'espressione regolare, letterale o costruttore, e memorizzarlo nella cache se deve essere riutilizzato". In altre parole, non fare affidamento su questa differenza di comportamento in envs sotto ES5 e continuare a memorizzare nella cache in modo appropriato in envs sopra :) – Pioul

+1

@Pioul, grazie per il feedback! Ho aggiornato la mia risposta e ho aggiunto come suggerito, ad eccezione della parte relativa al modello di costruzione. Vedi la mia risposta perché :) –

+0

Amen! : D – Pioul