Domanda

Come scriverei l'equivalente di String.StartsWith in JavaScript?

var haystack = 'hello world';
var needle = 'he';

haystack.startsWith(needle) == true

Nota: questa è una vecchia domanda e come sottolineato nei commenti ECMAScript 2015 (ES6) ha introdotto .startsWith . Tuttavia, al momento della stesura di questo aggiornamento (2015) il supporto del browser è lungi dall'essere completo .

È stato utile?

Soluzione

Puoi utilizzare la stringa di ECMAScript 6. prototype.startsWith () , ma è non ancora supportato in tutti i browser . Ti consigliamo di utilizzare uno shim / polyfill per aggiungerlo ai browser che non lo supportano. Creazione di un'implementazione conforme a tutti i dettagli di cui la specifica è un po 'complicata. Se vuoi uno shim fedele, usa:

Dopo aver rifiutato il metodo (o se stai supportando solo i browser e i motori JavaScript che già lo hanno), puoi usarlo in questo modo:

"Hello World!".startsWith("He"); // true

var haystack = "Hello world";
var prefix = 'orl';
haystack.startsWith(prefix); // false

Altri suggerimenti

Un'altra alternativa con .lastIndexOf :

haystack.lastIndexOf(needle, 0) === 0

Questo guarda indietro nel pagliaio per la presenza di ago a partire dall'indice 0 di pagliaio . In altre parole, controlla solo se pagliaio inizia con ago .

In linea di principio, ciò dovrebbe comportare vantaggi in termini di prestazioni rispetto ad altri approcci:

  • Non cerca nell'intero pagliaio .
  • Non crea una nuova stringa temporanea e quindi la scarta immediatamente.
data.substring(0, input.length) === input

Senza una funzione di supporto, basta usare .test :

/^He/.test('Hello world')

Per fare ciò con una stringa dinamica piuttosto che con una hardcoded (supponendo che la stringa non conterrà alcun carattere di controllo regexp):

new RegExp('^' + needle).test(haystack)

Dovresti dare un'occhiata a Esiste una funzione RegExp.escape in Javascript ? se esiste la possibilità che nella stringa compaiano caratteri di controllo regexp.

Migliore soluzione:

function startsWith(str, word) {
    return str.lastIndexOf(word, 0) === 0;
}

startsWith("aaa", "a")
true
startsWith("aaa", "ab")
false
startsWith("abc", "abc")
true
startsWith("abc", "c")
false
startsWith("abc", "a")
true
startsWith("abc", "ba")
false
startsWith("abc", "ab")
true

Ed ecco estremitàCon se ne hai bisogno anche tu:

function endsWith(str, word) {
    return str.indexOf(word, str.length - word.length) !== -1;
}

Per coloro che preferiscono prototiparlo in String:

String.prototype.startsWith || (String.prototype.startsWith = function(word) {
    return this.lastIndexOf(word, 0) === 0;
});

String.prototype.endsWith   || (String.prototype.endsWith = function(word) {
    return this.indexOf(word, this.length - word.length) !== -1;
});

Utilizzo:

"abc".startsWith("ab")
true
"c".ensdWith("c") 
true

Volevo solo aggiungere la mia opinione al riguardo.

Penso che possiamo usare così:

var haystack = 'hello world';
var needle = 'he';

if (haystack.indexOf(needle) == 0) {
  // Code if string starts with this substring
}

Ecco un piccolo miglioramento alla soluzione di CMS:

if(!String.prototype.startsWith){
    String.prototype.startsWith = function (str) {
        return !this.indexOf(str);
    }
}

"Hello World!".startsWith("He"); // true

 var data = "Hello world";
 var input = 'He';
 data.startsWith(input); // true

Verifica se la funzione esiste già nel caso in cui un browser futuro la implementi in codice nativo o se è implementata da un'altra libreria. Ad esempio, la Libreria prototipi implementa già questa funzione.

L'uso di ! è leggermente più veloce e più conciso di === 0 sebbene non così leggibile.

Dai un'occhiata anche a underscore.string.js . Viene fornito con una serie di utili metodi di test e manipolazione delle stringhe, incluso un metodo startWith . Dai documenti:

  

startWith _.startsWith (stringa, start)

     

Questo metodo controlla se stringa inizia con inizia .

_("image.gif").startsWith("image")
=> true

Di recente mi sono posto la stessa domanda.
Esistono diverse soluzioni possibili, eccone 3 valide:

Non ho trovato l'ultima soluzione che fa uso di un ciclo.
Sorprendentemente questa soluzione supera i primi 3 di un margine significativo.
Ecco il test jsperf che ho eseguito per giungere a questa conclusione:
http://jsperf.com/startswith2/2

Pace

ps: ecmascript 6 (harmony) introduce un metodo nativo startWith per le stringhe.
Basti pensare a quanto tempo sarebbe stato risparmiato se avessero pensato di includere questo tanto necessario metodo nella versione iniziale stessa.

Aggiorna

Come ha sottolineato Steve (il primo commento su questa risposta), la funzione personalizzata sopra genererà un errore se il prefisso dato è più corto dell'intera stringa. Ha risolto il problema e ha aggiunto un'ottimizzazione del ciclo che può essere visualizzata all'indirizzo http://jsperf.com/startswith2/4 .

Nota che ci sono 2 ottimizzazioni di loop che Steve ha incluso, il primo dei due ha mostrato prestazioni migliori, quindi posterò questo codice qui sotto:

function startsWith2(str, prefix) {
  if (str.length < prefix.length)
    return false;
  for (var i = prefix.length - 1; (i >= 0) && (str[i] === prefix[i]); --i)
    continue;
  return i < 0;
}

Dato che è così popolare, penso che valga la pena sottolineare che esiste un'attuazione per questo metodo nell'ECMA 6 e in preparazione a ciò si dovrebbe usare il polifill "ufficiale" per prevenire futuri problemi e lacrime.

Fortunatamente gli esperti di Mozilla ci forniscono uno:

https://developer.mozilla.org/ de / docs / Web / JavaScript / Reference / Global_Objects / String / startsWith

if (!String.prototype.startsWith) {
    String.prototype.startsWith = function(searchString, position) {
        position = position || 0;
        return this.indexOf(searchString, position) === position;
    };
}

Si noti che ciò ha il vantaggio di essere ignorato con garbo durante il passaggio a ECMA 6.

La soluzione più efficace è smettere di usare le chiamate in libreria e riconoscere semplicemente che si sta lavorando con due array. Un'implementazione eseguita a mano è breve e anche più veloce di ogni altra soluzione che ho visto qui.

function startsWith2(str, prefix) {
    if (str.length < prefix.length)
        return false;
    for (var i = prefix.length - 1; (i >= 0) && (str[i] === prefix[i]); --i)
        continue;
    return i < 0;
}

Per i confronti delle prestazioni (esito positivo e negativo), vedere http://jsperf.com/startswith2/4. (Assicurati di controllare le versioni successive che potrebbero aver battuto il mio.)

Ho appena imparato a conoscere questa libreria di stringhe:

http://stringjs.com/

Includi il file js e quindi usa la variabile S in questo modo:

S('hi there').endsWith('hi there')

Può anche essere usato in NodeJS installandolo:

npm install string

Quindi richiedendolo come variabile S :

var S = require('string');

La pagina web contiene anche collegamenti a librerie di stringhe alternative, se questa non ti piace.

var str = 'hol';
var data = 'hola mundo';
if (data.length >= str.length && data.substring(0, str.length) == str)
    return true;
else
    return false;

Sulla base delle risposte qui, questa è la versione che sto usando ora, in quanto sembra offrire le migliori prestazioni in base ai test JSPerf (ed è funzionalmente completa per quanto posso dire).

if(typeof String.prototype.startsWith != 'function'){
    String.prototype.startsWith = function(str){
        if(str == null) return false;
        var i = str.length;
        if(this.length < i) return false;
        for(--i; (i >= 0) && (this[i] === str[i]); --i) continue;
        return i < 0;
    }
}

Questo si basava su startWith2 da qui: http://jsperf.com/startswith2/6 . Ho aggiunto una piccola modifica per un piccolo miglioramento delle prestazioni e da allora ho anche aggiunto un controllo per la stringa di confronto che è nulla o non definita e l'ho convertita per aggiungerla al prototipo String usando la tecnica nella risposta di CMS.

Tieni presente che questa implementazione non supporta la "posizione" parametro menzionato in questo Mozilla Developer Network pagina, ma non sembra comunque far parte della proposta ECMAScript.

Non sono sicuro per JavaScript ma in dattiloscritto ho fatto qualcosa di simile

var str = "something";
(<String>str).startsWith("some");

Suppongo che dovrebbe funzionare anche su js. Spero che sia d'aiuto!

  1. La domanda è un po 'vecchia, ma volevo scrivere questa risposta per mostrarti alcuni parametri di riferimento che ho fatto sulla base di tutte le risposte fornite qui e del jsperf condiviso da Jim Buck.

Fondamentalmente avevo bisogno di un modo rapido per scoprire se un ago lungo si trova all'interno di un lungo pagliaio e sono molto simili tranne che per gli ultimi caratteri.

Ecco il codice che ho scritto che per ogni funzione (giuntura, sottostringa, startWith, ecc.) testa entrambi quando restituiscono false e true su una stringa di pagliaio ( nestedString ) di 1.000.0001 caratteri e una stringa dell'ago falsa o veritiera di 1.000.000 di caratteri ( testParentStringFalse e testParentStringTrue , rispettivamente):

// nestedString is made of 1.000.001 '1' repeated characters.
var nestedString = '...'

// testParentStringFalse is made of 1.000.000 characters,
// all characters are repeated '1', but the last one is '2',
// so for this string the test should return false.
var testParentStringFalse = '...'

// testParentStringTrue is made of 1.000.000 '1' repeated characters,
// so for this string the test should return true.
var testParentStringTrue = '...'

// You can make these very long strings by running the following bash command
// and edit each one as needed in your editor
// (NOTE: on OS X, `pbcopy` copies the string to the clipboard buffer,
//        on Linux, you would probably need to replace it with `xclip`):
// 
//     printf '1%.0s' {1..1000000} | pbcopy
// 

function testString() {
    let dateStart
    let dateEnd
    let avg
    let count = 100000
    const falseResults = []
    const trueResults = []

    /* slice */
    console.log('========> slice')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.slice(0, testParentStringFalse.length) === testParentStringFalse
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'slice',
        avg
    }
    console.log(`testString() slice = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.slice(0, testParentStringTrue.length) === testParentStringTrue
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'slice',
        avg
    }
    console.log(`testString() slice = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== slice')
    console.log('')
    /* slice END */

    /* lastIndexOf */
    console.log('========> lastIndexOf')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.lastIndexOf(testParentStringFalse, 0) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'lastIndexOf',
        avg
    }
    console.log(`testString() lastIndexOf = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.lastIndexOf(testParentStringTrue, 0) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'lastIndexOf',
        avg
    }
    console.log(`testString() lastIndexOf = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== lastIndexOf')
    console.log('')
    /* lastIndexOf END */

    /* indexOf */
    console.log('========> indexOf')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.indexOf(testParentStringFalse) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'indexOf',
        avg
    }
    console.log(`testString() indexOf = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.indexOf(testParentStringTrue) === 0
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'indexOf',
        avg
    }
    console.log(`testString() indexOf = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== indexOf')
    console.log('')
    /* indexOf END */

    /* substring */
    console.log('========> substring')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.substring(0, testParentStringFalse.length) === testParentStringFalse
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'substring',
        avg
    }
    console.log(`testString() substring = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.substring(0, testParentStringTrue.length) === testParentStringTrue
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'substring',
        avg
    }
    console.log(`testString() substring = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== substring')
    console.log('')
    /* substring END */

    /* startsWith */
    console.log('========> startsWith')
    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.startsWith(testParentStringFalse)
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    falseResults[falseResults.length] = {
        label: 'startsWith',
        avg
    }
    console.log(`testString() startsWith = false`, res, 'avg: ' + avg + 'ms')

    dateStart = +new Date()
    var res
    for (let j = 0; j < count; j++) {
        res = nestedString.startsWith(testParentStringTrue)
    }
    dateEnd = +new Date()
    avg = (dateEnd - dateStart)/count
    trueResults[trueResults.length] = {
        label: 'startsWith',
        avg
    }
    console.log(`testString() startsWith = true`, res, 'avg: ' + avg + 'ms')
    console.log('<======== startsWith')
    console.log('')
    /* startsWith END */

    falseResults.sort((a, b) => a.avg - b.avg)
    trueResults.sort((a, b) => a.avg - b.avg)

    console.log('false results from fastest to slowest avg:', falseResults)
    console.log('true results from fastest to slowest avg:', trueResults)
}

Ho eseguito questo test benchmark su Chrome 75 , Firefox 67 , Safari 12 e Opera 62 .

Non ho incluso Edge e IE perché non li ho su questa macchina, ma se qualcuno di voi vuole eseguire lo script su Edge e almeno IE 9 e condividere l'output qui, sarei molto curioso di vedere i risultati.

Ricorda solo che devi ricreare le 3 stringhe lunghe e salvare lo script in un file che poi apri nel tuo browser poiché copia / incolla sulla console del browser lo bloccherà poiché la lunghezza di ogni stringa è > = 1.000.000 ).

Ecco gli output:

Vince Chrome 75 ( sottostringa ):

false results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08271}
2)  {"label":"slice","avg":0.08615}
3)  {"label":"lastIndexOf","avg":0.77025}
4)  {"label":"indexOf","avg":1.64375}
5)  {"label":"startsWith","avg":3.5454}

true results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08213}
2)  {"label":"slice","avg":0.08342}
3)  {"label":"lastIndexOf","avg":0.7831}
4)  {"label":"indexOf","avg":0.88988}
5)  {"label":"startsWith","avg":3.55448}

Firefox 67 ( indexOf vince):

false results from fastest to slowest avg
1)  {"label":"indexOf","avg":0.1807}
2)  {"label":"startsWith","avg":0.74621}
3)  {"label":"substring","avg":0.74898}
4)  {"label":"slice","avg":0.78584}
5)  {"label":"lastIndexOf","avg":0.79668}

true results from fastest to slowest avg:
1)  {"label":"indexOf","avg":0.09528}
2)  {"label":"substring","avg":0.75468}
3)  {"label":"startsWith","avg":0.76717}
4)  {"label":"slice","avg":0.77222}
5)  {"label":"lastIndexOf","avg":0.80527}

Safari 12 ( slice vince per risultati falsi, inizia con vince per risultati reali, inoltre Safari è il più veloce in termini di tempo totale per eseguire l'intero test):

false results from fastest to slowest avg:
1) "{\"label\":\"slice\",\"avg\":0.0362}"
2) "{\"label\":\"startsWith\",\"avg\":0.1141}"
3) "{\"label\":\"lastIndexOf\",\"avg\":0.11512}"
4) "{\"label\":\"substring\",\"avg\":0.14751}"
5) "{\"label\":\"indexOf\",\"avg\":0.23109}"

true results from fastest to slowest avg:
1) "{\"label\":\"startsWith\",\"avg\":0.11207}"
2) "{\"label\":\"lastIndexOf\",\"avg\":0.12196}"
3) "{\"label\":\"substring\",\"avg\":0.12495}"
4) "{\"label\":\"indexOf\",\"avg\":0.33667}"
5) "{\"label\":\"slice\",\"avg\":0.49923}"
Vince

Opera 62 ( sottostringa . I risultati sono simili a Chrome e non mi sorprende poiché Opera si basa su Chromium e Blink):

false results from fastest to slowest avg:
{"label":"substring","avg":0.09321}
{"label":"slice","avg":0.09463}
{"label":"lastIndexOf","avg":0.95347}
{"label":"indexOf","avg":1.6337}
{"label":"startsWith","avg":3.61454}

true results from fastest to slowest avg:
1)  {"label":"substring","avg":0.08855}
2)  {"label":"slice","avg":0.12227}
3)  {"label":"indexOf","avg":0.79914}
4)  {"label":"lastIndexOf","avg":1.05086}
5)  {"label":"startsWith","avg":3.70808}

Si scopre che ogni browser ha i propri dettagli di implementazione (a parte Opera, che si basa su Chrome Chromium e Blink).

Certo, ulteriori test con diversi casi d'uso potrebbero e dovrebbero essere eseguiti (ad es. quando l'ago è molto corto rispetto al pagliaio, quando il pagliaio è più corto dell'ago, ecc ...), ma nel mio caso avevo bisogno di confrontare molto stringhe lunghe e volevo condividerla qui.

Se stai lavorando con startWith () e limitsWith () , devi fare attenzione agli spazi iniziali. Ecco un esempio completo:

var str1 = " Your String Value Here.!! "; // Starts & ends with spaces    
if (str1.startsWith("Your")) { }  // returns FALSE due to the leading spaces…
if (str1.endsWith("Here.!!")) { } // returns FALSE due to trailing spaces…

var str2 = str1.trim(); // Removes all spaces (and other white-space) from start and end of `str1`.
if (str2.startsWith("Your")) { }  // returns TRUE
if (str2.endsWith("Here.!!")) { } // returns TRUE

Puoi anche restituire tutti i membri di un array che iniziano con una stringa creando il tuo prototipo / estensione sul prototipo di array, aka

Array.prototype.mySearch = function (target) {
    if (typeof String.prototype.startsWith != 'function') {
        String.prototype.startsWith = function (str){
        return this.slice(0, str.length) == str;
      };
    }
    var retValues = [];
    for (var i = 0; i < this.length; i++) {
        if (this[i].startsWith(target)) { retValues.push(this[i]); }
    }
    return retValues;
};

E per usarlo:

var myArray = ['Hello', 'Helium', 'Hideout', 'Hamster'];
var myResult = myArray.mySearch('Hel');
// result -> Hello, Helium
scroll top