Добавление метода 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>
Я все еще не одобряю этот способ, потому что включение прототипов и отключение строгого режима замедлит ваш код.