现在有一个需求:

如果“一个字符串”超出“容器宽度”,将字符串截断最后一个字符、并在末尾加上‘...’ ,再次判断是否需要截断。直至字符串小于容器宽度。

下面SliceLabelStr函数采用了递归:
如果文字超出宽度,在attrStyle函数中将原字符截断末尾字符并加上‘...’ 再次调用sliceLabelStr判断是否超出宽度。
直至获得宽度合适的字符串并输出。

需要优化的递归函数
sliceLabelStr = function( textWidth, textStr) {
    var str;
   
    if(超出宽度) {
        dataLabel2 = attrStyle(textStr);
        var newTextWidth =  dataLabel2.text && dataLabel2.text.element.getBoundingClientRect().width;
        var textStr = dataLabel2.text.textStr;
        sliceLabelStr( newTextWidth, textStr);
    } else {
        str = attrStyle(textStr, true);
        return str; 
    }  
}

这样看起来没问题。其实需要优化很多地方。

1、递归没有出口

  1. 这个递归函数被调用了10遍,在11遍的时候符合了出口条件退出了,
    那么在第10遍没退出的时候,1到10次的函数都没有退出,只有在第11遍符合了出口条件return了以后,就会从11~1倒着依次退出

附上一张太阳神手写的解释图
图片描述

优化方法1
if(textWidth > containerWidth) {
        dataLabel2 = attrStyle(textStr);
        var newTextWidth =  dataLabel2.text && dataLabel2.text.element.getBoundingClientRect().width;
        var textStr = dataLabel2.text.textStr;
        return sliceLabelStr( newTextWidth, textStr);//有返回值
    } 

1、上述原因是递归没有出口,而且if语句中调用本函数递归的时候没有返回值。

2、有时会产生“调用栈溢出”的情况。

2、在if判断条件中,由于在某种情况下textWidth没有变化造成了无限递归

3、用do...while 代替 递归

sliceLabelStr = function(forX2Array, textWidth, textStr) {
    var str,
        sliceStr = textStr.text && textStr.text.textStr || textStr;

    if(compareTextLength(forX2Array, textWidth)) {
        var n=0;

        do{
            n++;
        }
        while(sliceStr.substr(0,n)!=sliceStr && attrLabel2Style(forX2Array[0], sliceStr.substr(0,n)).getBBox()["width"]<ContainerWidth(forX2Array))

        return sliceStr.substr(0,n);

    } else {
        str = attrLabel2Style(forX2Array[0], sliceStr, true);
        return str;
    }
}

逻辑比较直观,简单能用循环的就用循环,递归一般用在复杂算法,比如弗洛伊德,A+之类

4、这是SVGElement,如果是普通的DOM元素。可以用如下方法。

图片描述


aprildove
233 声望15 粉丝

一个前端