Może głupie pytanie: jak umieścić w definicji funkcji stałą, przez wartość zmiennej w momencie definiowania

0

Dzień dobry. Mam taki problem. Może głupi i dla początkujących:

Mam taki kod:

var elements = ["prod", "direct", "count", "comp"];
var i = -1;
for (i = 0; i < elements.length; i++)
    document.getElementsByTagName("form")[0].elements[elements[i]].addEventListener("keyup",
        function (e)
        {
            var numberOfChars = this.value.length; // - this.value.getSelection().length;
            var hit = getFirstOrderedElement(elements[i], this.value);

            if (e.keyCode != 8 && e.keyCode != 16 && e.keyCode != 17  && e.keyCode != 18)
            {
                this.value = (hit == "") ? this.value : hit;
                this.setSelectionRange(numberOfChars, -1);
            }
        });

Chodzi o to, że umieściłem w definicji funkcji zmienną i która istnieje poza funkcją. Kiedy for przeleci, zmienna i = 4. A ja bym chciał, żeby funkcje zdefiniowane przeze mnie korzystały nie, z wartości aktualnej, tylko z wartości i, znanej w momencie definiowania, czyli, 0, 1, 2, 3. Jak to zrobić?

PS. Pytanie nadal aktualne, ja jednak znalazłem inne rozwiązanie: Jako, że mam tylko 4 pola do obróbki, stworzyłem sobie, z palca, 4 funkcje i je, z palca przypisałem. Nie jest to eleganckie, ale trudno.

function autoComplete(event, th, n)
{
    var numberOfChars = th.value.length; // - th.value.getSelection().length;
    var hit = getFirstOrderedElement(elements[n], th.value);

    if (event.keyCode != 8 && event.keyCode != 16 && event.keyCode != 17  && event.keyCode != 18)
    {
        th.value = (hit == "") ? th.value : hit;
        th.setSelectionRange(numberOfChars, -1);
    }
}

function fProd(event)
{
    autoComplete(event, this, 0);
}

function fDirect(event)
{
    autoComplete(event, this, 1);
}

function fCount(event)
{
    autoComplete(event, this, 2);
}

function fComp(event)
{
    autoComplete(event, this, 3);
}

document.getElementsByTagName("form")[0].elements[elements[0]].addEventListener("keyup", fProd);
document.getElementsByTagName("form")[0].elements[elements[1]].addEventListener("keyup", fDirect);
document.getElementsByTagName("form")[0].elements[elements[2]].addEventListener("keyup", fCount);
document.getElementsByTagName("form")[0].elements[elements[3]].addEventListener("keyup", fComp);

Pytanie nadal aktualne

Dzięki
Michał

2

Zadeklaruj zmienną i za pomocą słowa kluczowego let. Zmienne zadeklarowane z użyciem var mają zasięg funkcji, w której są zadeklarowane, dlatego w chwili obsłużenia eventu zmienna i ma taką wartość, jak po zakończeniu działania pętli.

0

Dzięki za odpowiedź. Nie do końca rozumiem.

Tzn. też wczoraj czytałem o słówku let. Ale jak je wykorzystać. Tzn. jak przypiszę zmiennej let n = i; to i tak funkcja będzie czytać wartość zmiennej i. Proszę o przykład użycia.

Dzięki
Michał

3

Kod, który korzysta ze zmiennej i, ma za pomocą domknięcia dostęp do zasięgu, w którym ta zmienna się znajduje. Jeśli zmienna jest zadeklarowana z użyciem słowa kluczowego var, to w momencie jej użycia ma już osiągniętą wartość ostateczną, bo funkcja wykonuje się dopiero po wykonaniu wszystkich kroków pętli. Jeśli użyte jest słowo let, to tworzone są osobne zasięgi i wtedy Twój kod zachowuje się tak, jakby został napisany bez pętli w taki sposób, jak przedstawiam poniżej. Dla każdej iteracji pętli tworzony jest nowy zasięg dla zmiennej i (zasięg bloku), a w każdym z tym zasięgów zmienna ma inną wartość. Dla uproszczenia w przykładzie korzystam z wyrażenia funkcji, które od razu się wykonuje i z funkcji setTimeout, która powoduje, że wywołanie zwrotne wykonywane jest później, niż od razu (nawet jeśli czas to 0) - czyli podobnie jak w przypadku eventów.

(function() {
    var i = 0; //użycie "let" w tym przypadku nie zmieni wyniku - też będzie "3 3 3", bo zasięgi są identyczne
    for (i = 0; i < 3; i++) {
        setTimeout(function () {
            console.log(i); //3 3 3
        }, 0);
    }
})();

Ale taki kod (zauważ, że deklaracja jest tym razem wewnątrz pętli, czyli inaczej niż w kodzie powyżej):

(function() {
    for (let = 0; i < 3; i++) {
        setTimeout(function () {
            console.log(i); //0 1 2
        }, 0);
    }
})();

można zapisać w taki sposób:

(function() {
    {
        //użycie "var" spowodowałoby, że pierwsza deklaracja zostanie przeniesiona na początek 
        //funkcji przez hoisting, a każda kolejna deklaracja zostanie zignorowana, więc 
        //klamry przestaną mieć znaczenie i zostanie wyświetlone "2 2 2" (bo ostateczna wartość zmiennej "i" to 2)

        let i = 0; 
        setTimeout(function () {        
            console.log(i); //0
        }, 0);
    }

    {
        let i = 1;
        setTimeout(function () {
            console.log(i); //1
        }, 0);
    }

    {
        let i = 2;
        setTimeout(function () {    
            console.log(i); //2
        }, 0);
    }
})();

Podobne działanie można osiągnąć bez użycia let:

(function() {
    for (var i = 0; i < 3; i++) {
        setTimeout((function (x) {
            console.log(x); //0 1 2
        }).bind(null, i), 0);
    }
})();

Funkcja setTimeout przyjmuje też parametry (trzeci i kolejne), które przekaże dalej jako argumenty dla funkcji, która jest wywołaniem zwrotnym, więc podobny efekt można osiągnąć bez użycia funkcji bind, ale w Twoim przykładzie wywołanie zwrotne jest wykonywane przez funkcję obsługującą event, dlatego bind będzie odpowiednim rozwiązaniem.

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