字符串连接

标准的方式都是A+B通过+进行连接,如果是下面这种写法:

str +=  'one' + 'two';

这段代码会经历四个步骤:

  1. 先在内存中创建一个新的临时字符串(内容为空)
  2. one和two先进行连接并赋值给临时字符串
  3. 临时字符串与str进行连接
  4. 返回的值赋值给str

如果我们这样写可以避免创建临时字符串

str +=  'one';
str +=  'two';

因为只有一个字符串需要添加,然后str本身就是字符串,所以只对str进行了操作,没有再去创建新的临时字符。

但是这样写太过麻烦,于是可以这样写:

str = str + 'one' + 'two';

因为字符的操作是从=符号开始,从左到右操作,此时str本身是一个占据在内存中的字符串,所以无需创建新的临时字符,然后依次往右连接,最后赋值,为此可以加快速度。

但是在ie7及更早的版本,这种方式反倒没有加速的效果,反而会增加用时,原因是ie每次连接的时候都会每进行一步就复制一次,先将str和one连接的结果复制一次,复制一次后再复制后面的two,然后再将这两个连接的结果复制一次,依次 重复,所以在旧版的ie中,使用+=反倒更快了。

但是+=也不能说明就是ie的最佳方案,在大量文字连接中,+=也是很慢,但是我们可以通过数组项合并的方式提高速度。

数组项合并

Array.prototype.join方法是将数组中的分隔符改变成其他的值,我们可以通过去除这个分隔符达到合并数组的效果,但是这个方法实际上在现在浏览器中是一个比较慢的方法,在旧版的ie反倒可以达到意料之外的效果。

var str = 'I',
    strs = [],
    num = 5000,
    newStr = null;
while(num--) {
  strs.push(str);
}
newStr = strs.join('');

这样写反倒快于用for循环str+ ‘I’的方法。当然也仅限于旧版的ie。

正则表达式

正则表达式的优化要首先明白他的工作原理,最主要的就是理解回溯。

回溯就是当正则匹配到一定的值后会继续查找正则后面的值,比如/.*\s+/;首先正则会贪婪的将所有字符匹配到,然后进行回溯,也就是从最后一个字符往前查找,当找到一个\s是并不会立即放弃,它还会继续回溯,直到找不到\s字符了。

如果是惰性模式/.*?\s+/,它的方式又不一样了,惰性可以理解为最少次数,所以他会从最开始一个一个字符回溯,找到一个\s后并不会立即停止,它还会继续查找,直到后面的字符不是\s后才会停止。

使用不明确的正则容易造成回溯失控,你可以把回溯理解为路口,当你到了一个站点后,就会根据实际情况产生多个不同的选择,每个选择为一个路口,当正则在第一个路口找不到的时候就会返回到第二个路口,然后无限重复,直到找到了值或者全部路口都找不到才会停下,所以说,能找到的正则时间最短,找不到的正则是最慢的,因为他要不断的回溯。

为此宽泛的正则性能往往不如更具体的选择,.*往往不如[white]这种实际指出的值,然后我们还可以通过一些位置符减少回溯的次数,比如\b、\B这种。

var str = 'AAAAAAAAAAAAAAAAAAAAAAAAAA';
var reg = /A+A+B/g;
var time = +new Date();

alert(reg.test(str) +' ' + (+new Date() - time))

这段代码中,如果str没有最后的B字符,正则依旧会不断的重复回溯很多次,A+A+会产生众多的路口,为此我们要考虑到一种情况,就是当str中没有B的时候该怎么办?

这里我们就可以使用预查?!

var str = 'AAAAAAAAAAAAAAAAAAAAAAAAAA';
var reg = /A+A+(?=B)B/g;
var time = +new Date();

alert(reg.test(str) +' ' + (+new Date() - time))

通过这个预查可以减少回溯的点,因为预查已经判断到了A+A+的部分,后面是要有B的,那么正则就不会再一个个判断,为此减少了时间,alert输出的时候,你会发现使用之前的正则返回1毫秒的时间,而使用预查的只用了0毫秒,速度非常之快,如果在大量文本内容的时候,预查的优势也会很高,但是不能滥用,预查也是可以进行一定的优化的。

捕获

正则中的捕获()也就是分组会将里面的内容额外的保存起来,如果大量不用的捕获,会浪费内存的空间,为此可以使用非捕获来减少空间占用。

/^[a-z]+(?:.*)/

通过非捕获?:来达到减少占用。

事实上预查虽然是一个(),但是他是非捕获性的,所以他不能被回调所调用。

何时不使用正则

其实这里主要是因为正则只能从开始匹配到末尾,没办法从末尾匹配到开头,哪怕你使用了$字符申明,但是他依旧是从前到后,所以如果是针对末尾的操作,使用其他方法会更快。

比如去除文字末尾的空白字符

var str = 'isdjaajda    ',
    reg = /\s/,
    end = str.length - 1;
while(reg.test(str.charAt(end))) {
    end-- ;
 }
str = str.slice(0,end+1);

string的操作方法,位置是从0开始计数的,然后获取的是该位置的左边,0 = |i;用|表示获取的位置,于是end就等于文字的length-1获得最后一位的位置,最后当end判断到不是空白字符时,end实际上等于|a这里,使用slice获取区间时要拿到最后一个a,所以end+1。

slice获取到就是末尾没有空白字符的值,如果你使用正则的话想对来说会比较慢一些,特别是前面很多字符的情况。

混合解决方法

正则开头匹配很快,后面很慢,那么就可以混合使用,达到优化的效果

function trim(str) {
  var str = str.replace(/^\s+/,''),
      end = str.length -1,
      reg = /\s/;

  while(reg.test(str.charAt(end))) {
      end--;
  }

  return str.slice(0,end + 1);
}

trim('      isdjaajda    ');
分类: javascript高性能 标签: 字符正则表达式

评论

暂无评论数据

暂无评论数据

目录