Wykorzystanie RegExp zamiast Stringów

0

W skrypcie mam pełno odwołań do jednej funkcji na stringach
Dla zobrazowania zagęszczenia tego powiedzmy że skrypt wygląda mniej więcej tak:

funkcja('string');

alert(funkcja('string2'));

obiekt.a = funkcja('dane');
obiekt.b = funkcja('dane2');

function test()
{
    alert(funkcja('ok'));
}

function zapisz()
{
    obiekt.a = funkcja('foo');
    obiekt.b = obiekt.c ? funkcja('bar') : funkcja('foobar');
}

funkcję tę wykonuję na KAŻDYM stringu - dlatego też myślałem żeby to uładnić
mógłbym stworzyć prototyp z funkcją "funkcja" i wtedy wyglądałoby to tak:

'string'.funkcja();

alert('string2'.funkcja());

obiekt.a = 'dane'.funkcja();
obiekt.b = 'dane2'.funkcja();

function test()
{
    alert('ok'.funkcja());
}

function zapisz()
{
    obiekt.a = 'foo'.funkcja();
    obiekt.b = obiekt.c ? 'bar'.funkcja() : 'foobar'.funkcja();
}

jak dla mnie trochę czytelniejsze i ładniejsze bo mniej jest zagęszczonych nawiasów
zmiana nazwy funkcji na krótszą, powiedzmy "f" nie wchodzi w grę bo niepotrzebnie wizualnie komplikuje kod, a na jego prostocie dla użytkownika końcowego zależy mi najbardziej

najfajniej by było gdyby dało się wykonać funkcję po prostu dla każdego stringa
można nadpisać funkcję .toString() ale wywoływana jest ona tylko kiedy string jest obiektem a nie typem prostym, czyli musiałbym dawać wszędzie:

new String('przykład')

ale można za to wykorzystać składnię do tworzenia wyrażeń regularnych

/regExp/

nadpisując RegExp.prototype.toString można skrypt sprowadzić do postaci:

/string/;

alert(/string2/);

obiekt.a = /dane/;
obiekt.b = /dane2/;

function test()
{
    alert(/ok/);
}

function zapisz()
{
    obiekt.a = /foo/;
    obiekt.b = obiekt.c ? /bar/ : /foobar/;
}

i wszystko działa i jest jak dla mnie fajnie
ale chciałbym spytać co Wy o tym sądzicie? Czy jest jakiś konkretny powód dla którego w żadnym wypadku nie powinienem tak robić?

0

aha, stringi są wykorzystywane tylko do prezentacji, nie będą porównywane ani nic w tym stylu więc mam pewność że do moich zastosować toString zawsze się odpali

0

Jeśli ta funkcja, którą wywołujesz, to funkcja tłumacząca -- czyli po prostu kodujesz obsługę wielu wersji językowych ("internacjonalizację") -- to ten problem został już jakoś rozwiązany i nawet istnieje pewna konwencja.

Nazwij po prostu funkcję _ (czyli sam znak podkreślenia). Ta konwencja jest używana w gettext i różnych innych systemach internacjonalizacji. Używasz wtedy normalnie _('Foobar').

Jeśli nie kodujesz internacjonalizacji, to możesz wybrać inną, globalną funkcję o krótkiej nazwie. Na siłę możesz rozszerzyć String.prototype, ale wg mnie, skoro funkcja jest super często używana i jest jedyną taką funkcją, to można złamać anty-globalną ;) politykę i uciec się do funkcji globalnej, tak jak to robi np. jQuery z funkcją $. Podobnie robią inne frameworki, jak Prototype czy underscore.js. To ostatnie używa akurat znaku podkreślenia, co może być mylące, ale konwencja, że _('Tekst') oznacza internacjonalizację jest dużo starsza niż underscore.js, więc można ten problem olać.

Niemal na pewno nie używałbym regexpów. Ta składnia wygląda już totalnie myląco. Musiałbyś mieć super mocne uzasadnienie. Proponuję Ci napisać co konkretnie robisz, bo -- jak widzisz na przykładzie internacjonalizacji -- ma to znaczenie.

0

tak - to funkcja tłumacząca
nie chciałem tego wspominać bo bałem się że ktoś będzie na siłę mi podsuwał rozwiązanie całkiem zmieniające podejście a które by nie pasowało w moim przypadku
ale skoro konwencja ze znakiem podkreślenia jest szerzej rozpowszechniona to jej własnie będę używał

dzięki

ale mam jeszcze pytanie - masz może jakieś namiary na statystyki na ile popularne jest wykorzystanie underscore.js
chciałbym żeby mój framework nie gryzł się z żadnym innym, więc jeśli jest dość popularny to musiałbym pomyśleć nad czymś innym - może dwa znaki podkreślenia? __('string') też jeszcze nie wygląda najgorzej

0

Możesz zastosować sposób z jQuery. Czyli tak jakby dwie nazwy ("przestrzenie nazw") biblioteki: jedną długą, opisową, która nigdy nie powinna wejść w konflikt z czymkolwiek (oni mają jQuery, Ty np. translate) i drugą krótką, wygodną (oni $, Ty możesz mieć _ lub t).

W jQuery jest jeszcze funkcja noConflict(). Przywraca ona "poprzednią" (tj. potencjalnie ustawioną przez inną bibliotekę) funkcję $ i zwraca referencję do obiektu jQuery.

Ty mółbyś zaimplementować to podobnie. Ktoś używałby tego tak:

// wcześniej includowano underscore.js, a po niej
// Twoją bibliotekę, która też normalnie używa funkcji _()

// tutaj funkcja _() należy do Twojej biblioteki
alert(_("tekst"));

var translatazor = translate.noConflict();

// po powyższym wywołaniu noConflict() Twoja biblioteka
// przywróciła funkcję _() jako tą z underscore.js

// tutaj można już używać underscore:
_(["a", "b", "c"]).forEach(...);

Jak to się implementuje? Ano np. tak:

(function() {
  // this wskazuje tu na obiekt globalny, czyli zwykle window
  var global = this;

  var originalUnderscore = global._; // zapamiętujemy window._

  var translate = function(msgid) { 
    return "przetłumaczony tekst na bazie msgid"; // to nasza funkcja tłumacząca, odpowiednik $() lub jQuery()
  };

  translate.noConflict = function() {
    global._ = originalUnderscore; // w funkcji noConflict() przywracamy oryginalną wartość _()
    return translate;
  };

  global.translate = global._ = translate; // ustawiamy window._() na naszą funkcję tłumaczącą
}());

Żeby uniknąć konfliktu z underscore, wystarczy wtedy, że ktoś użyje na początku swojego kodu:

translate.noConflict();

// od teraz _() jest takie, jak było przed dołączeniem translate, czyli np. to z underscore
// można nadal używać długiej nazwy translate():

alert(translate("tekst"));

// a można też było zrobić od razu swojego aliasa:
var myTranslate = translate.noConflict();
alert(myTranslate("tekst"));

Co do tego, jakiej krókiej nazwy użyć... Nie będę podejmował za Ciebie decyzji, sam miałbym zagwożdżkę. Może i lepsza t() -- też krótka, a z underscore.js się nie kojarzy. Funkcja t() też jest niekiedy wykorzystywana przy tłumaczeniu. Np. jako funkcja-tag w systemie szablonów.

PS. Cały kod pisałem z palca, więc sorki, jeśli są jakieś błędy/literówki.

0

dzięki za rozpisanie się (choć to w Twoim przypadku normalne ;))
ale raczej nie mogę wykorzystać tego sposobu u siebie
jQuery faktycznie ma dwie nazwy, ale "wewnątrz siebie" używa tej dłuższej nazwy i tylko w ten sposób ma to prawo działać
u mnie wszystko będzie się działo jakby "wewnątrz" więc musiałbym za każdym razem przepisywać do _ krótką wersję i przy kończeniu funkcji przywracać starą wersję, trochę się w ogóle imo traci sens
dobra na razie zostaję przy _, pod koniec będę się tym martwić

myślałem też nad rozszerzeniem samego obiektu _ tak żeby działał jako proxy dla oryginalnego obiektu poza główną funkcją, która by się różnie zachowywała w zależności od typu argumentu
zobaczy się potem, pozdr

1 użytkowników online, w tym zalogowanych: 0, gości: 1