Προς το περιεχόμενο

HTML/ JS script (closures)


gifour

Προτεινόμενες αναρτήσεις

Καλησπέρα σας,

Διάβαζα σχετικά με τα closures της JS όταν έφτασα σ' ενα παράδειγμα που χρησιμοποιούσε ένα button για να αυξάνει έναν counter. Όλα καλά, ακολούθησα το παράδειγμα και δούλεψε. Μάλλον το κατάλαβα...

! :-)

Σαν ενθουσιώδης νέος σκέφτηκα να επεκτείνω το παράδειγμα προσθέτοντας άλλο ένα button που θα μειώνει τον counter. Αυτό ήταν... από το πρωί δεν έχω ξεκολλήσει.

Στον κώδικα που παραθέτω χρειάζεται να πατηθούν και τα δύο buttons για να εμφανίζει το αποτέλεσμα. Πατώντας μόνο το ένα δεν δίνει τίποτα, παρότι από κάτω δείχνει να δουλεύει.  'Οταν πατηθούν και τα δύο από μια φορά "ξεκολλάει" και 'ολα παίζουν μετά.

 

Νομίζω πως το πρόβλημα οφείλεται στις variables counter1 , counter2 αλλά δεν έχω καταφέρει να το εντοπίσω. Αν τις δηλώσω έξω από τη myFunction δουλεύει αέρα, αλλά απ' ότι κατάλαβα ο σκοπός είναι να τις έχεις μέσα στο local scope. 

 

 

Αν έχετε να κάνετε κάποια υπόδειξη, να δώσετε μια παραπομπή ή μια εξήγηση,  please do it!

<!DOCTYPE html>
<html>
<body>

<p>Counting with a local variable.</p>

<p id="demo"></p>
<button type="button" onclick="myFunction('+')">Count +</button>
<button type="button" onclick="myFunction('-')">Count -</button>
<p id="c1">Counter1</p>
<p id="c2">Counter2</p>

<script>
document.getElementById("demo").innerHTML = 0;
function add(number){
    var counter = 0;
    return function() {return counter += number;} 
}
function sub(number){
    var counter = 0;
    return function() {return counter -= number;} 
}

var counterIncr = add(1);
var counterDicr = sub(1);

function myFunction(s){
	if( s == "+"){
	counter1 = counterIncr();
	}
	else if (s == "-"){
	counter2 = counterDicr();
	}
    
    if(counter1 && counter2){
 		document.getElementById("demo").innerHTML = (counter1 + counter2);}
    if(counter1 && !counter2){
        document.getElementById("demo").innerHTML = counter1;}
    if(!counter1 && counter2){
		document.getElementById("demo").innerHTML = counter2;}
    if(!counter1 && !counter2){
        document.getElementById("demo").innerHTML = "N/A";}
}
</script>

</body>
</html>

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Για αρχή που είναι τα

var counter1;
var counter2;

που τα δηλώνεις; 

 

Επίσης το παρακάτω ξέρεις τι ακριβώς σημαίνει

if(counter1 && counter2)

Αν το counter1 ΚΑΙ το counter2 είναι TRUE. Κάπου έχεις μπερδευτεί μου φαίνεται. 

 

Επίσης, αν οι μεταβλητές δεν δηλωθούν έξω από τη συνάρτηση, κάθε φορά που καλείς μια συνάρτηση τότε αυτή παίρνει ξανά την ίδια τιμή με πριν. 

 

Π.χ. στο παρακάτω το counter είναι ΠΑΝΤΑ ίσο με το 0 πριν κάνεις την πράξη. Και γιατί το καλείς μέσα σε function και όχι απλά ως "return counter += number;"

function add(number){
    var counter = 0;
    return function() {return counter += number;} 
}
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Το καλεί μέσα σε function για να δει πως δουλεύουν τα closures. Πιθανότατα ο κώδικας είναι αντιγραφή/προέκταση κάποιου υπάρχοντος παραδείγματος.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

@rafinos

Όπως είπα στην αρχή, το αντικείμενο του παραδείγματος είναι είναι το closure στην JS. Αυτό μάλλον μεταφράζεται, νοηματικά, σαν "τερματισμός" στην προκειμένη περίπτωση (αν κάποιος ξέρει την ελληνική λέξη με διορθώνει).

Απ' ότι κατάλαβα μελετώντας το, που μπορεί να έχω καταλάβει και λάθος, είναι πως χρησιμοποιείς αυτή την μέθοδο "προστασίας" μεταβλητών όταν δεν θέλεις να έχεις τις συγκεκριμένες μεταβλητές σου εκτεθημένες στο global execution context. 

Αν πρόσεξες, η add() επιστρέφει μια anonymous function η οποία παρακάτω γίνεται assign με argument "1" στη var counterIncr. Αντίστοιχα και sub() στην counterDicr.

Τι πετυχαίνει αυτό; Η συνάρτηση add() (και η sub() αντίστοιχα) "τρέχουν" μία μόνο φορά για το argument "1", όταν τις καλείς για τα

var counterIncr = add(1);
var counterDicr = sub(1);

και μετά εξαφανίζονται από το execution stack. Αυτό σημαίνει πως όταν στο run time εμείς πατάμε τα buttons αυτές οι συναρτήσεις δεν καλούνται ξανά, οπότε δεν ξαναμηδενίζουν την var counter. 

Από την άλλη πλευρά η JS engine διατηρεί προσβάσιμο στη μνήμη το lexical execution context των add() και sub() ώστε κάθε φορά που πατάμε τα buttons και καλούμε τις counterIncr() και counterDicr() να μπορούν να βρούν την var counter κοιτάζοντας το parental execution context.

 

Πάμε τώρα στη myFunction η οποία, κρύβει το μυστήριο για μένα, και παίρνει δύο arguments "+" ή "-".

Αν πάρει "+" θέλω να τρέχει την counterIncr() που αυξάνει τον counter1,

αν παρει "-" θέλω να τρέχει την counterDicr() που μειώνει τον counter2

στο τέλος και υπό συνθήκες, ώστε να γίνεται η πράξη "πρόσθεση" χωρίς να εμπλέκονται undef 's, Null 's, NaN's κλπ, θέλω να δίνει στο #demo το αποτέλεσμα της πρόσθεσης.

 

Λοιπόν το παράξενο εδώ είναι πως το προσδοκούμενο έρχεται μόνο όταν πατήσεις και τα δύο buttons(?). Αν πατάς μόνο το ένα... παίρνεις το κενό("0"), μόλις πατήσεις και το δεύτερο δουλεύει τέλεια, όχι μόνο από εκείνο το σημείο και μετά αλλά σου φέρνει και όλα τα πατήματα που έχεις κάνει μέχρι εκείνη τη στιγμή και έδειχνε πως δεν τα έπαιρνε χαμπάρι.

 

Αν έχεις διάθεση να το δεις, τρέξτο λίγο .


Το καλεί μέσα σε function για να δει πως δουλεύουν τα closures. Πιθανότατα ο κώδικας είναι αντιγραφή/προέκταση κάποιου υπάρχοντος παραδείγματος.

Σωστά 

Link.png Site: http://www.w3schools.com/js/tryit.asp?filename=tryjs_function_counter (ήταν λάθος πριν και το διόρθωσα)

Επεξ/σία από gifour
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Δες τα error στο browser.

Αν πατήσεις το + σου πετάει : Uncaught ReferenceError: counter2 is not defined

 

 

Κάτι τέτοιο θες να κάνεις, φαντάζομαι.

<!DOCTYPE html>
<html>
<body>

<p>Counting with a local variable.</p>

<p id="demo"></p>
<button type="button" onclick="myFunction('+')">Count +</button>
<button type="button" onclick="myFunction('-')">Count -</button>
<p id="c1">Counter1</p>
<p id="c2">Counter2</p>

<script>
document.getElementById("demo").innerHTML = 0;
function add(number){
    var counter = 0;
    return function() {return counter += number;} 
}
function sub(number){
    var counter = 0;
    return function() {return counter -= number;} 
}

var counterIncr = add(1);
var counterDicr = sub(1);

function myFunction(s){

	if( s == "+"){
		counter1 = counterIncr();
	}
	else if (s == "-"){
		counter2 = counterDicr();
	}
    
	if(typeof counter1 ==='undefined') {
		document.getElementById("demo").innerHTML = counter2;
	}else if (typeof counter2 === 'undefined') {
		document.getElementById("demo").innerHTML = counter1;
	}else {
		document.getElementById("demo").innerHTML = (counter1 + counter2);
	}
}
</script>

</body>
</html>
Επεξ/σία από djcat
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Τελικά είναι σωστό πως όταν περιγράφεις το πρόβλημα  βρίσκεις τη λύση.

Το λάθος όντως ήταν στη MyFunction.

Παραθέτω τη version που δουλεύει με το "this".

Παρ' όλα αυτά δεν έχω καταλάβει ακόμη πως δούλευε μετά το δεύτερο πάτημα.

<!DOCTYPE html>
<html>
<body>

<p>Counting with a local variable.</p>

<p id="demo"></p>
<button type="button" onclick="myFunction('+')">Count +</button>
<button type="button" onclick="myFunction('-')">Count -</button>
<p id="c1">Counter1</p>
<p id="c2">Counter2</p>

<script>
document.getElementById("demo").innerHTML = 0;

function add(number){
    var counter = 0;
    return (function() {return counter += number;});
}
function sub(number){
    var counter = 0;
    return (function() {return counter -= number;});
}

var counterIncr = add(1);
var counterDicr = sub(1);

function myFunction(s){
	
    if( s == "+"){
	this.counter1 = counterIncr();
	}
	else if (s == "-"){
	this.counter2 = counterDicr();
	}
    
    if(this.counter1 && this.counter2){
 		document.getElementById("demo").innerHTML = (this.counter1 + this.counter2);}
    if(this.counter1 && !this.counter2){
        document.getElementById("demo").innerHTML = this.counter1;}
    if(!this.counter1 && this.counter2){
		document.getElementById("demo").innerHTML = this.counter2;}
    if(!this.counter1 && !this.counter2){
        document.getElementById("demo").innerHTML = "N/A";}
}
</script>

</body>
</html>

Δες τα error στο browser.

Αν πατήσεις το + σου πετάει : Uncaught ReferenceError: counter2 is not defined

 

 

Κάτι τέτοιο θες να κάνεις, φαντάζομαι.

function myFunction(s){

    if( s == "+"){
        counter1 = counterIncr();
    }
    else if (s == "-"){
        counter2 = counterDicr();
    }

    if( counter1 && typeof counter2 === 'undefined'){
document.getElementById("demo").innerHTML = counter1;
    }
    else if( typeof counter1 ==='undefined' && counter2){
        document.getElementById("demo").innerHTML = counter2;
    }else if(counter1 && counter2){
        document.getElementById("demo").innerHTML = (counter1 + counter2);
    }

 

Ναι, αλλά έχω την εντύπωση πως το undefined δίνει πάντα false και δεν υπάρχει ανάγκη γαι το typeof.

 

Σ' ευχαριστώ που έδωσες χρόνο να το ψάξεις κι εσύ.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Ο κώδικας σου ο πρώτος σκάει.

Η εντύπωση σου δεν είναι σωστή. 

Κοίταξε πως κάνεις debug στη javascript.

Αν θες να ασχοληθείς με προγραμματισμό, είναι απαραίτητο!

 

*Δες τις αλλαγές που έκανα στον κώδικα που παρέθεσα. Τώρα παίζει και αν πατήσεις πρώτα (-).

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Εννιά στους δέκα προγραμματιστές JavaScript δεν καταλαβαίνουν πως δουλεύουν τα closures. Μην περιμένεις να τα καταλάβεις με την πρώτη. Ρίξε και μια ματά εδώ:

https://github.com/getify/You-Dont-Know-JS/tree/master/scope%20%26%20closures

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Εννιά στους δέκα προγραμματιστές JavaScript δεν καταλαβαίνουν πως δουλεύουν τα closures. Μην περιμένεις να τα καταλάβεις με την πρώτη. Ρίξε και μια ματά εδώ:

https://github.com/getify/You-Dont-Know-JS/tree/master/scope%20%26%20closures

Thanks for this 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...