Question

Comment puis-je écrire l'équivalent de la String.StartsWith en JavaScript?

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

haystack.startsWith(needle) == true

Remarque: Cette question est ancienne et, comme indiqué dans les commentaires, ECMAScript 2015 (ES6) a introduit le .startsWith . Cependant, au moment de la rédaction de cette mise à jour (2015), la prise en charge du navigateur est loin d’être complète .

Était-ce utile?

La solution

Vous pouvez utiliser la String de ECMAScript 6. prototype.startsWith () , mais il s'agit d'une pas encore pris en charge par tous les navigateurs . Vous voudrez utiliser un shim / polyfill pour l'ajouter sur les navigateurs qui ne le prennent pas en charge. Créer une implémentation conforme à tous les détails décrits dans la spécification est un peu compliquée. Si vous voulez une cale fidèle, utilisez l’une des méthodes suivantes:

Une fois que vous avez calé la méthode (ou si vous ne supportez que les navigateurs et les moteurs JavaScript qui en disposent déjà), vous pouvez l'utiliser comme ceci:

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

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

Autres conseils

Une autre alternative avec .lastIndexOf :

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

Ceci regarde en arrière dans haystack pour rechercher une occurrence de aiguille à partir de l'index 0 de haystack . En d'autres termes, il vérifie uniquement si haystack commence par needle .

En principe, cela devrait présenter des avantages en termes de performances par rapport à d'autres approches:

  • Il ne cherche pas dans la botte de foin entière.
  • Il ne crée pas de nouvelle chaîne temporaire et ne la supprime pas immédiatement.
data.substring(0, input.length) === input

Sans fonction d'assistance, il suffit d'utiliser le de Regex / regex / regex .test méthode:

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

Pour ce faire, utilisez une chaîne dynamique plutôt qu'une chaîne codée en dur (en supposant que la chaîne ne contiendra aucun caractère de contrôle d'expression rationnelle):

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

Vous devriez vérifier Existe-t-il une fonction RegExp.escape en Javascript ? s'il existe une possibilité que des caractères de contrôle d'expression rationnelle apparaissent dans la chaîne.

Meilleure solution:

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

Et voici endsWith si vous en avez aussi besoin:

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

Pour ceux qui préfèrent le prototyper dans 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;
});

Utilisation:

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

Je voulais juste ajouter mon opinion à ce sujet.

Je pense que nous pouvons simplement utiliser comme ceci:

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

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

Voici une amélioration mineure à la solution de 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

Vérifier si la fonction existe déjà au cas où un futur navigateur l'implémenterait en code natif ou si elle était implémentée par une autre bibliothèque. Par exemple, la bibliothèque de prototypes implémente déjà cette fonction.

Utiliser ! est légèrement plus rapide et plus concis que === 0 bien que ce ne soit pas aussi lisible.

Vérifiez également le underscore.string.js . Il est livré avec un tas de méthodes de test et de manipulation de chaînes utiles, notamment une méthode startsWith . À partir de la documentation:

  

startsWith _. startsWith (chaîne, commence)

     

Cette méthode vérifie si chaîne commence par commence .

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

Je me suis récemment posé la même question.
Il existe plusieurs solutions possibles, voici 3 solutions valables:

Je n'ai pas rencontré la dernière solution qui utilise une boucle.
Étonnamment, cette solution surpasse de loin les 3 premiers par une marge significative.
Voici le test jsperf que j'ai effectué pour parvenir à cette conclusion:
http://jsperf.com/startswith2/2

Paix

ps: ecmascript 6 (harmonie) introduit une méthode native startsWith pour les chaînes.
Imaginez combien de temps aurait été économisé s’ils avaient pensé à inclure cette méthode indispensable dans la version initiale.

Mettre à jour

Comme Steve l’a souligné (premier commentaire de cette réponse), la fonction personnalisée ci-dessus renvoie une erreur si le préfixe indiqué est plus court que la chaîne entière. Il a corrigé cela et ajouté une optimisation de boucle qui peut être consultée à l'adresse http://jsperf.com/startswith2/4 .

Notez qu'il y a 2 optimisations de boucle que Steve a incluses, le premier des deux affichait de meilleures performances. Je vais donc publier le code ci-dessous:

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

Étant donné sa popularité, je pense qu’il est utile de souligner qu’il existe une implémentation de cette méthode dans ECMA 6 et que, pour ce faire, il convient d’utiliser le polyfill "officiel" afin de prévenir les problèmes futurs et les déchirures.

Heureusement, les experts de Mozilla nous en ont fourni un:

https://developer.mozilla.org/ de / docs / Web / JavaScript / Référence / Global_Objects / String / startsWith

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

Veuillez noter que cela présente l'avantage d'être ignoré gracieusement lors de la transition vers ECMA 6.

La meilleure solution consiste à ne plus utiliser les appels de bibliothèque et à reconnaître que vous utilisez deux baies. Une implémentation manuelle est à la fois courte et plus rapide que toutes les autres solutions que j'ai vues ici.

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

Pour des comparaisons de performances (succès et échecs), voir http://jsperf.com/startswith2/4 (Assurez-vous de vérifier les versions ultérieures qui ont peut-être pris le mien.)

Je viens d'apprendre à propos de cette bibliothèque de chaînes:

http://stringjs.com/

Incluez le fichier js puis utilisez la variable S comme ceci:

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

Il peut également être utilisé dans NodeJS en l’installant:

npm install string

Ensuite, en tant que variable S :

var S = require('string');

La page Web contient également des liens vers d'autres bibliothèques de chaînes, si celle-ci ne vous tente pas.

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

D'après les réponses fournies, voici la version que j'utilise maintenant, car elle semble offrir les meilleures performances basées sur les tests JSPerf (et est fonctionnellement complète à ce que je sache).

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

Ceci était basé sur startsWith2 à partir d'ici: http://jsperf.com/startswith2/6 . J'ai ajouté une petite astuce pour une amélioration minime des performances, et depuis lors, j'ai également vérifié la chaîne de comparaison comme étant nulle ou indéfinie, et l'a convertie pour l'ajouter au prototype String en utilisant la technique de la réponse du CMS.

Notez que cette implémentation ne prend pas en charge la " position " paramètre mentionné dans ce Réseau de développeurs Mozilla page, mais cela ne semble de toute façon pas faire partie de la proposition ECMAScript.

Je ne suis pas sûr pour le javascript, mais dans le manuscrit j'ai fait quelque chose comme

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

Je suppose que cela devrait fonctionner aussi sur js. J'espère que ça aide!

  1. La question est un peu ancienne, mais je voulais écrire cette réponse pour vous montrer quelques repères que j'ai établis sur la base de toutes les réponses fournies ici et du commentaire partagé par Jim Buck.

J'avais besoin d'un moyen rapide de savoir si une longue aiguille se trouvait dans une longue botte de foin et qu'elles étaient très similaires, à l'exception des derniers caractères.

Voici le code que j'ai écrit qui, pour chaque fonction (épissure, sous-chaîne, startsWith, etc.) teste les deux quand ils renvoient false et true par rapport à une chaîne de haystack ( nestedString ) de 1.000.0001 caractères et une chaîne d'aiguilles fausses ou vraies de 1.000.000 caractères ( testParentStringFalse et testParentStringTrue , respectivement):

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

J'ai effectué ce test d'évaluation sur Chrome 75 , Firefox 67 , Safari 12 et Opera 62 .

Je n'ai pas inclus Edge et IE parce que je ne les ai pas sur cette machine, mais si quelqu'un d'entre vous veut exécuter le script contre Edge et au moins IE 9 et partager le résultat ici, je serais très curieux de voir les résultats.

N'oubliez pas que vous devez recréer les 3 longues chaînes et enregistrer le script dans un fichier que vous ouvrez ensuite dans votre navigateur sous forme de copier / coller sur la console du navigateur. Il sera bloqué car la longueur de chaque chaîne est > = 1.000.000. ).

Voici les sorties:

Chrome 75 ( sous-chaîne gagne):

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 gagne):

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 (la tranche l'emporte pour les faux résultats, commenceAvec pour obtenir les vrais résultats, également Safari est le plus rapide en termes de temps total d'exécution du 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}"

Opera 62 ( sous-chaîne gagne. Les résultats sont similaires à ceux de Chrome et je ne suis pas surpris car Opera est basé sur Chromium et 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}

Il s'avère que chaque navigateur a ses propres détails d'implémentation (à part Opera, basé sur Chrome et Blink de Chrome).

Bien sûr, des tests supplémentaires avec différents cas d'utilisation pourraient et devraient être effectués (par exemple, lorsque l'aiguille est vraiment courte comparée à la botte de foin, lorsque la botte de foin est plus courte que l'aiguille, etc.), mais dans mon cas, je devais comparer très longues chaînes et je voulais le partager ici.

Si vous travaillez avec startsWith () et endsWith () , vous devez être prudent en ce qui concerne les espaces. Voici un exemple complet:

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

Vous pouvez également renvoyer tous les membres d'un tableau commençant par une chaîne en créant votre propre prototype / extension dans le prototype du tableau, à savoir

.
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;
};

Et pour l'utiliser:

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