日期:2014-05-16  浏览次数:20404 次

String.prototype.replace 的 javascript 实现比较

?

chrome :

??

以前初学java时,总是被建议多看 jdk源码可以大大增强功力,后来想把这套也搬到javascript的学习过程中,无奈发现本来应该算作javascript 基础api范畴的 String , Array ... 等都是 native code ,但现在不一样了,chrome V8 来了,我不懂他是怎么实现的 ,但是 javascript 中的基础 api 都是用 javascript 自身实现的(alert(String.prototype.replace);),和 java 越来越接近了,大概由于 v8 的作者 以前就是搞jvm的,觉得这些东西还是像 java 一样作为工具包提供比较好吧。

?

V8 源码@google code

?

V8 作者的解释:

?

All library functions are implemented in JavaScript
Examples: Array.prototype.join, String.prototype.replace


The benefits of not implementing it inside the engine:
1.Keeps the core engine cleaner
2.Easier to change and extend
3.Capitalizes on the performance of the JavaScript compiler


One drawback is startup time ~30 ms ...(每次启动编译库代码)


启动慢的解决之道:

V8 allows saving the initial heap in a snapshot
1.Serialized form is very fast to read
2.Includes pre-compiled code for JavaScript builtins
3.Integrated in the build system and embedded directly in V8


With snapshots, the startup time is reduced to 4-8 ms


???? String.prorotype.replace 应该还算复杂,特别是 replacement可以为函数这个特性是java等静态没有的。


????? 看一下String的replace的主要框架实现吧(最复杂的正则表达式 + 函数替换 ),加了点注释便于理解。详见:String.js

?

?

/*
	regexp 正则表达式
	replace 为函数 function(match,capture1,capture2 ...){
	}
*/
function StringReplaceRegExpWithFunction(subject, regexp, replace) {
  //先做一次正则match,因为下面循环中需要前一次的匹配结束位置,便于写循环
  var lastMatchInfo = DoRegExpExec(regexp, subject, 0);
  if (IS_NULL(lastMatchInfo)) return subject;
	
	/*
		类似于Stringbuilder ,便于从subject截取指定范围子串,存在自己的数组
		generate 时用常见的 join 即可。
	*/
  var result = new ReplaceResultBuilder(subject);
  /*
  	如果正则表达式带g,要全部替换
  */
  if (regexp.global) {
    //初始上一次匹配成功位置为 0
    var previous = 0;
    do {
      
      //上一次匹配位置到这次匹配成功开始位置要放在结果里
      result.addSpecialSlice(previous, lastMatchInfo[CAPTURE0]);
      
      //当前匹配成功开始位置,match.index
      var startOfMatch = lastMatchInfo[CAPTURE0];
      
      //当前匹配成功结束位置,相当于 regexp.lastIndex
      previous = lastMatchInfo[CAPTURE1];
      
      
      //当前匹配传给replacement函数得到替换结果加入结果集合
      result.add(ApplyReplacementFunction(replace, lastMatchInfo, subject));
      
      
      //如果只匹配一个字。。。相当于 regexp == /[\s\S]/
      if (previous == startOfMatch) {
        // 确实没越界,加入结果集合
        if (previous < subject.length) {
          result.addSpecialSlice(previous, previous + 1);
        }
        //下次就要从previous开始加入集合了。
        previous++;
      }

      //没越界,继续分析
      lastMatchInfo = (previous > subject.length)
          ? null
          : DoRegExpExec(regexp, subject, previous);
          
    //没有match就结束      
    } while (!IS_NULL(lastMatchInfo));


		//最后一次匹配结束位置到字符串结尾
    if (previous < subject.length) {
      result.addSpecialSlice(previous, subject.length);
    }
  } 
  
  //不是 g 正则,简单
  else { 
    result.addSpecialSlice(0, lastMatchInfo[CAPTURE0]);
    var endOfMatch = lastMatchInfo[CAPTURE1];
    resul