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