正在加载中

最后更新于 2019年06月14日

字符串连接

标准的方式都是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    ');
  • weixiao kaixin tushetou jingkong deyi fanu liezui liuhan daku ganga bishi nanguo lihai qian yiwen numu tu yi haixiu se fadai minyan hehe henkaixin huaji biyiyan kuanghan maimeng shui xiaku penqi zhangzui pen aini ye niu laji ok chigua renshi kongbu shuai xiaoxiese touxiao huaixiao jingnu chihuai kaisang xiaoku koubi zhuangbi lianhong kanbujian shafa zhijing xiangjiao dabian yaowan redjing lazhu rizhi duocang chixigua hejiu xixi xiaopen goukun xiaobuchu shenme wusuowei guancha lajing chouyan xiaochi bie zhadanzui zhadanxiao