Добавление метода replaceAll в класс ActionScript String

Мне нужна помощь в добавлении замены всех функций в мой проект Flex. Я бы предпочел сделать это настолько естественным, насколько это возможно.

Чего я хочу добиться, так это иметь возможность запускать этот код (с помощью компилятора Flex) "aabbaaba".replaceAll("b","c") и получить "aaccaaca", Тоже хочу цепочку replaceAll звонки.
Примечание: я не буду заменять b с c s, но различные строки, которые не будут известны во время кодирования!

Что я не хочу:
1. Используйте регулярные выражения с глобальным флагом. Токены для замены определяются во время выполнения объявления, и преобразование их в регулярное выражение не является простым.
2. Используйте StringUtil.replace метод. Это статический метод, и цепочка ужасна.
3. Цепочка split а также join, Потому что это может сбивать с толку других при чтении кода.
4. Отключите строгую проверку типов. Я хочу иметь проверку типов для остальной части моего кода.


Вот что у меня так далеко:

String.prototype.replaceAll = function(replace:String, replaceWith:String):String{
    return this.split(replace).join(replaceWith);
}

И можно назвать так:

"aababacaaccb"["replaceAll"]("b", "c")["replaceAll"]("c", "a");

Кроме того, есть ли какие-либо рекомендации против расширения объектов через прототип? Я также приму ответ, который имеет веские аргументы против расширения объекта String через прототип.

Спасибо,
Алинь

7 ответов

Решение

Я думаю, что вы получили все возможные технические ответы. Я подробно остановлюсь на том, что я считаю лучшим подходом к этому языку.

Прототипы не рекомендуются для языка ООП, такого как AS3 (в основном потому, что они не поддаются инкапсуляции). Вы подразумеваете, что не хотите, чтобы что-то "сбивало с толку других" (по отношению к split.join); Что ж, прототипы в AS3 очень запутанные. Так же, как пример этого, объявление прототипа может быть сделано из любого места в вашем коде, и как таковое не очевидно, где оно должно находиться. Если "другие" сталкиваются с "foo".replaceAll() в вашем коде, совершенно не очевидно, где можно найти этот метод и проверить, что он действительно делает.

Статическая функция исправляет это и довольно проста. Конечно, вам нужен дополнительный аргумент, и вы не можете правильно связать, но разве это плохо?

Если вам нужна производительность, рекомендуется использовать split.join. Я бы поспорил на хорошие деньги, которые больше разработчиков AS3 знают о split.join, чем об использовании прототипов.

С другой стороны, я думаю, что наиболее семантическим и прагматичным способом было бы использование метода собственного языка (отсюда мой предыдущий ответ). Вы пытаетесь заменить все иглы в строке другой строкой в ​​AS3, и для этого у языка есть метод String::replace с глобальным флагом. Я уверен, что есть способ легко разобрать и использовать любую строку в регулярном выражении.

Я согласен, что в некоторых случаях может потребоваться вспомогательный метод (например, replaceAll), но я настоятельно рекомендую вам не использовать прототипы, а вместо этого использовать более стандартный способ, такой как StringUtil.replace.

Второй ответ таков:

напишите класс-оболочку StringEx для String, и вы можете определить replaceAll, который будет соединен в цепочку следующим образом

public function replaceAll(replace:String, replaceWith:String):StringEx {
   string = string.replace(new RegExp(replace, 'g'), replaceWith);
   return this;
}

предоставляя toString(), вы можете легко изменить объект StringEx на String

var result:String = "" + new StringEx("aaccaaca").replaceAll('b', 'c').replaceAll('c', 'a');

Вы можете получить полную версию здесь: ООП способ расширения прототипа

Чего я не хочу: 1. Использовать регулярные выражения с глобальным флагом

Почему бы и нет? для простых замен строк достаточно просто и производительно:

"aababacaaccb".replace(/b/g, "c");

или если вы предпочитаете:

var needle:String="b";
"aababacaaccb".replace(new RegExp(needle,"g"), "c");

Я бы не советовал использовать прототип для этого, он не очень ООП и не стандартный... он слишком хакерский для такой простой операции.

Я не могу придумать способ удовлетворить 4 требования, которые вы опубликовали. Но я думаю, что если вашей главной целью является замена различных токенов за один раз (чего вы хотели добиться с помощью цепочки вызовов), при этом используя любую строку в качестве токена, вы можете попробовать что-то вроде этого:

public class StringUtil {
    public static function replaceAll(source:String,map:Object):String {
        for(var replaceToken:String in map) {
            source = source.split(replaceToken).join(map[replaceToken]);
        }
        return source;
    }
}

var str:String = "a1ccca111a";

str = StringUtil.replaceAll(str,{
    'a' : 'b',
    '1' : '2'
});
trace(str);

Есть одна оговорка, которую нужно иметь в виду. С объектом Object порядок не гарантируется. В большинстве случаев это не важно, но если в вашем случае это так, вы можете вместо этого использовать массив, и в каждом слоте храните как токен, так и его замену (например, как объект).

Я знаю, что это старая ветка, но это то, что я придумал, которая может сделать любую обычную строку.

    public static function replaceAll(p_string:String, p_search:String, p_replaceWith:String):String{

    //Dummy Control
        if(p_string == null)
            return '';

    //Iterates through the string from right to left so we don't go over what we are changing.
        var index:int = p_string.lastIndexOf(p_search);
        while(index != -1){

        //Splits the string at the index
            p_string = p_string.slice(0, index) + p_replaceWith + p_string.slice(index+p_search.length);

        //Attempts to find the next index
            index = p_string.lastIndexOf(p_search, index-1);//We -1 so we always move down the line
        }

    //Returns the modified p_string
        return p_string;
    }

"aabbaaba".split('b').join('c') возвращает 'ааккаака'

UPD:
Интересно, почему это неприемлемо для вас (однако оно не поддерживает регулярные выражения): оно работает довольно быстро (проверено в серьезных циклах) и делает именно то, что вы хотите.

а также есть пара решений, которые не упомянуты в вашем черном списке (кстати, статическая функция лучше, чем возиться с прототипом - imho):

  • Вы можете расширить класс String только replaceAll метод и super() вызов. экземпляры этого класса вернутся true если вы проверите (myStringInstance is String)
  • и другое решение: создайте оболочку вместо расширения: класс, который будет хранить строку для вас, обеспечивая дополнительную функциональность (хотя я не могу представить ее для строки) и, возможно, сделать некоторые из ее свойств доступными только для чтения. или он может иметь только геттер и сеттер для одного

У меня есть два ответа для вас. Первым будет то, что вы хотите. Но я рекомендую второе.

Включение прототипов в flex очень просто и может быть сделано путем установки flex-config

ReplaceAllTest.as

package {
    import flash.display.Sprite;
    import flash.text.TextField;
    public class ReplaceAllTest extends Sprite
    {
        public function ReplaceAllTest() 
        {
            var tf:TextField = new TextField;
            String.prototype.replaceAll = function(replace:String, replaceWith:String):String{
                return this.split(replace).join(replaceWith);
            }
            // now the strict mode is off compiler does NOT warn the following code
            tf.text = "aababacaaccb".replaceAll("b", "c")
                                    .replaceAll("c", "a");
            addChild(tf);
        }
    }
}

ReplaceAllTest-config.xml

<?xml version="1.0" encoding="utf-8" ?>
<flex-config>
    <compiler>
       <as3>false</as3>
       <es>true</es>
       <strict>false</strict>
    </compiler>
    <static-link-runtime-shared-libraries>true</static-link-runtime-shared-libraries>
</flex-config>

Я все еще не одобряю этот способ, потому что включение прототипов и отключение строгого режима замедлит ваш код.

Другие вопросы по тегам