我该如何编写与 C# 相同的内容 String.StartsWith 在 JavaScript 中?

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

haystack.startsWith(needle) == true

笔记:这是一个老问题,正如评论中指出的那样 ECMAScript 2015 (ES6) 引入了 .startsWith 方法。然而,在撰写本更新时(2015 年) 浏览器支持还远未完成.

有帮助吗?

解决方案

您可以使用 ECMAScript 6 String.prototype.startsWith() 方法,但它是 尚未在所有浏览器中支持. 。您需要使用 shim/polyfill 将其添加到不支持它的浏览器上。创建一个符合以下要求的实现 规范中列出的所有细节 有点复杂。如果您想要一个忠实的垫片,请使用:

一旦你调整了该方法(或者如果你只支持已经拥有该方法的浏览器和 JavaScript 引擎),你可以像这样使用它:

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

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

其他提示

另一种选择是 .lastIndexOf:

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

这看起来向后通过 haystack 对于发生 needle 从索引开始 0haystack. 。换句话说,它只检查是否 haystack 以。。开始 needle.

原则上,这应该比其他一些方法具有性能优势:

  • 它不会搜索整个 haystack.
  • 它不会创建新的临时字符串然后立即丢弃它。
data.substring(0, input.length) === input

如果没有辅助功能,只需使用正则表达式的 .test 方法:

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

要使用动态字符串,而不是硬编码的一个(假定串将不包含任何正则表达式的控制字符)这样做:

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

您应该检查是否有一个JavaScript RegExp.escape功能?如果可能性是存在的正则表达式的控制字符出现在字符串中

最佳的解决方案:

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

这是的endsWith 如果您需要它:

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

:用于那些宁愿它原型到字符串:

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

<强>用法:

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

我只是想补充一点关于这个我的意见。

我想我们可以只使用这样的:

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

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

下面是一个小规模的改善到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

检查功能是否已经存在的情况下的未来的浏览器实现它在本机代码,或者如果它是由另一个库实现。例如,原型库已实现此功能。

使用!略大于=== 0更快和更简洁虽然不及可读

还检查了 underscore.string.js 。它配备了大量有用的字符串测试和操作方法,包括startsWith方法。从文档:

  

<强> startsWith _.startsWith(string, starts)

     

此方法检查是否string始于starts

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

我最近问自己同样的问题。
有多种可能的解决方案,这里有 3 个有效的解决方案:

  • s.indexOf(starter) === 0
  • s.substr(0,starter.length) === starter
  • s.lastIndexOf(starter, 0) === 0 (在看到马克·拜尔斯的 回答)
  • 使用循环:

    function startsWith(s,starter) {
      for (var i = 0,cur_c; i < starter.length; i++) {
        cur_c = starter[i];
        if (s[i] !== starter[i]) {
          return false;
        }
      }
      return true;
    }
    

我还没有遇到最后一个使用循环的解决方案。
令人惊讶的是,这个解决方案的性能明显优于前 3 个解决方案。
这是我为得出这个结论而进行的 jsperf 测试: http://jsperf.com/startswith2/2

和平

附:ecmascript 6(和谐)引入了原生 startsWith 字符串的方法。
试想一下,如果他们想到在初始版本本身中包含这种急需的方法,将会节省多少时间。

更新

正如史蒂夫指出的(对此答案的第一条评论),如果给定的,上面的自定义函数将抛出错误 字首 比整个字符串短。他已经修复了这个问题并添加了循环优化,可以在以下位置查看 http://jsperf.com/startswith2/4.

请注意,Steve 包含了 2 个循环优化,其中第一个显示了更好的性能,因此我将在下面发布该代码:

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

由于这是如此受欢迎,我认为这是值得指出的是,有在ECMA 6和对一个人应该为了防止未来的问题和泪水使用“官方”填充工具编写本方法的实现。

幸运的是Mozilla的专家为我们提供了一个:

https://developer.mozilla.org/ DE /文档/网络/ JavaScript的/参考/ Global_Objects /字符串/ startsWith

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

请注意,这具有的对得到的过渡摆好忽略,以ECMA 6的优点。

最好的高性能解决方案是停止使用库调用和刚刚认识到,你有两个数组工作。手卷实现短期和也比我在这里看到所有其他解决方案快。

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

有关的性能比较(成功和失败),请参见 http://jsperf.com/startswith2/4。 (请务必检查更高版本可能已经胜过我的。)

我刚刚得知这个字符串库:

http://stringjs.com/

包括JS文件,然后使用S变量是这样的:

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

它也可以在通过的NodeJS安装它使用:

npm install string

然后需要它作为S变量:

var S = require('string');

在网页也有链接到备用字符串库,如果这一个不拿你的想象。

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

根据这里的答案,这是我现在使用的版本,因为它似乎提供了基于 JSPerf 测试的最佳性能(并且据我所知功能完整)。

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

这是基于这里的startsWith2: http://jsperf.com/startswith2/6. 。我添加了一个小调整以实现微小的性能改进,并且此后还添加了对比较字符串为空或未定义的检查,并使用 CMS 答案中的技术将其转换为添加到字符串原型。

请注意,此实现不支持本节中提到的“位置”参数 Mozilla 开发者网络 页面,但这似乎并不属于 ECMAScript 提案的一部分。

我不知道的JavaScript,但在打字稿我不喜欢的东西。

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

我想这应该JS工作了。 我希望它能帮助!

  1. 这个问题有点老了,但我想写这个答案来向您展示我根据此处提供的所有答案以及 Jim Buck 共享的 jsperf 所做的一些基准测试。

我基本上需要一种快速的方法来查找长针是否在长干草堆中,并且除了最后一个字符之外,它们非常相似。

这是我编写的代码,对于每个函数(splice、substring、startsWith 等),当它们针对 haystack 字符串返回 false 和 true 时进行测试(nestedString) 1.000.0001 个字符以及 1.000.000 个字符的假或真针串 (testParentStringFalsetestParentStringTrue, , 分别):

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

我运行了这个基准测试 铬75, 火狐67, 狩猎12歌剧 62.

我没有包含 Edge 和 IE,因为我的机器上没有它们,但如果你们中有人想针对 Edge 和至少 IE 9 运行脚本并在此处共享输出,我会非常想看到结果。

请记住,您需要重新创建 3 个长字符串并将脚本保存在一个文件中,然后在浏览器中打开该文件,因为在浏览器控制台上复制/粘贴会阻止它,因为每个字符串的长度 >= 1.000.000)。

以下是输出:

铬75(substring 获胜):

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}

火狐 67(indexOf 获胜):

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 因错误结果而获胜, startsWith 就执行整个测试的总时间而言,Safari 是最快的):

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

歌剧 62 (substring 获胜。结果与 Chrome 类似,我并不感到惊讶,因为 Opera 基于 Chromium 和 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}

事实证明,每个浏览器都有自己的实现细节(Opera 除外,它基于 Chrome 的 Chromium 和 Blink)。

当然,可以而且应该对不同的用例进行进一步的测试(例如当针与干草堆相比非常短时,当干草堆比针短时,等等...),但就我而言,我需要比较很长的字符串并想在这里分享它。

如果您正在使用startsWith()endsWith()工作,那么你必须要小心前导空格。下面是一个完整的示例:

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

您还可以通过创建自己的原型/扩展到数组原型返回一个字符串开头的阵列的所有成员,又名

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

和使用它:

var myArray = ['Hello', 'Helium', 'Hideout', 'Hamster'];
var myResult = myArray.mySearch('Hel');
// result -> Hello, Helium
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top