JavaScript WTF Vol. 2 – ანონიმური ფუნქციები

ანონიმური ფუნქციები JavaScript-ში ერთერთი მოქნილი და სასარგებლო საშუალებაა თუმცა პერიოდულად გარკვეული სირთულეები მათი გამოყენებისას მაინც გვხვდება.

ამჯერად ყურადღება მინდა გავამახვილო ანონიმური ფუნქციისა და თვით იმ ფუნქციის კონტექსტზე რომლის შიგნითაც ხდება ანონიმური ფუნქციის შექმნა.

მაგალითისათვის განვიხილოთ შემდეგი კოდი:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function init() {
   
    //private variable
    var privateVar = "This is private variable";
   
    //public variable
    this.publicVar = "This is public variable";
   
    //private method
    var privateMethod = function() {
        alert("This is private method");
    }
   
    //public method
    this.publicMethod = function() {
        alert("This is public method");
    }
   
    //initialize anchor onclick
    var anchor = document.getElementById("test");
    anchor.onclick = function() {
        alert(privateVar); //alert private variable
        alert(publicVar); //alert public variable
        privateMethod(); //call private method
        publicMethod(); //call public method
    }
   
}

//window onload event calls init function
window.onload = init;

ასევე ამ მაგლითისთვის საჭიროა შემდეგი HTML კოდი:

1
<a href="#" id="test">Test onclick</a>

განვიხილოთ init() ფუნქცია რომელიც დოკუმენტის სრულად ჩატვირთვის თანავე გამოიძახება (იხ. window.onload = init;). მას გააჩნია რამდენიმე private და public წევრი. ასევე ამ ფუნქციის მეშვეობით ხდება a ელემენტის onclick მოვლენისათვის (ანონიმური)ფუნქციის მინიჭება, რომელიც შესრულებისას test() ფუნქციის ყველა წევრს მიმართავს(იძახებს) მიმდევრობით. თუ ამ კოდს შევასრულებთ ერთმანეთის მიყოლებით მივიღებთ შეტყობინებებს:

  • This is private variable
  • This is public variable
  • This is private method
  • This is public method

ერთი შეხედვით ყველაფერი ნათელია და ყველაფერი სწორად მუშაობს. ასევე შეგვიძლია გავაკეთოთ დასკვნა რომ init() ფუნქციის კონტექსტს აბსოლუტურად იზიარებს anchor.onclick – ისთვის განსაზღვრული ანონიმური ფუნქცია. თუმცა კოდს თუ გავუკეთებთ მცირე მოდიფიკაციას დავინახავთ რომ სურათი რადიკალურად იცვლება და ამან შეიძლება გარკვეული პრობლემები წარმოშვას გარკვეულ შემთხვევებში, კოდის ბოლო სტრიქონი ჩავანაცვლოთ შემდეგი კოდით:

1
2
3
4
5
6
function callInit() {
  var testInit = new init();
}

//window onload event calls callInit function
window.onload = callInit;

ამჯერად a ელემენტზე დაკლივის შემდეგ მივიღებთ მხოლოდ ერთ შეტყობინებას:

  • This is private variable

და კოდი შეწყვეტს მუშაობს შემდეგი შეტყობინებით “publicVar is not defined”… რა მოხდა ამ შემთხვევაში? onclick ელემენტის ანონიმურმა ფუნქციამ დაკარგა კავშირი init() ფუნქციასთან, მაგრამ მხოლოდ ნაწილობრივ? თუ init() ფუნქციის private წევრებზე აქვს წვდომა რატომ public წევრებზე არ აქვს წვდომა? სიმართლე რომ ითქვას ბოლომდე ვერ გავერკვიე ამ დეტალებში და გარკვეულ წილად ბუნდოვანი დარჩა რაღაც დეტალები რომ აქ ავხსნა, მაგრამ რამდენიმე(იმედია სწორი) დასკვნის გაკეთება შეგვიძლია აქედან:

  • ანონიმური ფუნქცია იზიარებს init ფუნქციის კონტექსტს მთლიანად(აქვს წვდომა მის ყველა წევრთან) მიუხედავად იმისა წევრი არის public თუ private.
  • ყველაფერი ერთ კონტექსტში სრულდება და init ფუნქციის ყველა წევრი სტატიკურია და არ საჭიროების init – ის ახალ ეგზემპლარს.
  • თუ init არ გამოიძახება პირდაპირ, არამედ მოხდება მისი ახალი ობიექტის(new init()) შექმნა, onclick – ისთვის განსაზღვრული ანონიმური ფუნქცია ნაწილობრივ გაიზიარებს init ფუნქციის კონტექსტს, არ დაკარგავს წვდომას მის private წევრებთან მაგრამ დაკარგავს წვდომას მის public წევრებზე, ანუ აღმოჩნდება ობიექტის(რომელიც იქმნება new ოპერატორით) კონტექსტს მიღმა

არის თუ არა ასეთი მდგომარეობიდან გამოსავალი? დიახ არის და შემდეგ მაგალითში ნაჩვენებია ასეთი შემთხვევა:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function init() {
   
    //private variable
    var privateVar = "This is private variable";
   
    //public variable
    this.publicVar = "This is public variable";
   
    //private method
    var privateMethod = function() {
        alert("This is private method");
    }
   
    //public method
    this.publicMethod = function() {
        alert("This is public method");
    }
   
    //initialize anchor onclick
    var anchor = document.getElementById("test");
   
    //create reference to current object
    var ref = this;
   
    anchor.onclick = function() {
      var initRef = ref; //create reference to object
        alert(privateVar);
        alert(initRef.publicVar); //now call publicVar through initRef object reference
        privateMethod();
        initRef.publicMethod(); //now call publicMethod through initRef object reference
    }
   
}

function callInit() {
  var testInit = new init();
}

//window onload event calls callInit function
window.onload = callInit;

მოდიფიცირებულ კოდში ყურადღება უნდა მივაქციოთ var ref = this; ნაწილს, რადგან 1) ამ კოდს ვწერთ onclick ანონიმური ფუნქციის გარეთ; 2) onclick ანონიმურ ფუნქციაში ვქმნით ახალ initRef ცვლადს და მას ვანიჭებთ ref ცვლადს (var initRef = ref;); 3) init – ის public წევრებს მივმართავთ initRef – ის საშუალებით;

რეზულტატი აბსოლუტურად იგივეა რაც იყო პირველ შემთხვევაში თუმცა აქ უნდა გავამახვილოთ ყურადღება ref=this; კოდზე, რატომ დავწერეთ ეს კოდი onclick ანონიმური ფუნქციის გარეთ და არა თვით ფუნქციაში მაგ. var initRef = this;? ასე რომ დაგვეწერა ფუნქციის შიგნით this უკვე ამ ფუნქციაზე მიუთითებდა და არა init ფუნქციის რომელიმე ეგზემპლარზე, და წვდომა init – ის ობიექტთან კვლავ დაიკარგებოდა.

შეიძლება ბუნდოვნად და რთულად ავხსენი ეს პრობლემა, თუმცა კიდევ ვიმეორებ გარკვეული საკითხები ჩემთვის ისევ ბურუსით არის მოცული.

ტეგები: ,

One Response to “JavaScript WTF Vol. 2 – ანონიმური ფუნქციები”

  1. kalosha says:

    მშვენიერია,

    განსაკუთრებით ბოლოში this განხილვა

    კალოშა

დატოვე კომენტარი:

ქართული კლავიატურა, ჩართვა/გამორთვა კლავიშით "~"