[ Web Proxy ]
URL:
Viewing: https://uz.javascript.info/function-object [Back]  [Original]

Funktsiya obyekti, NFE
UZ

Ushbu ochiq manbali loyihani butun dunyo bo'ylab odamlar uchun taqdim etmoqchimiz.

Ushbu qo'llanmaning mazmuni sizning tilingizga tarjima qilishga yordam bering!

    Javascript.info saytidan qidirish:
    O'quv qo'llanmasidan qidiring:
    Light themeDark theme
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    Bizga malumki, JavaScript-dagi funktsiyalar qiymatlardir.

    JavaScript-dagi har qanday qiymat turga ega. Funktsiya qaysi turga kiradi?

    JavaScript-da funktsiyalar obyektlardir.

    Funktsiyalarni tasavvur qilishning yaxshi usuli bu chaqiriladigan harakat obyektlari. Biz ularni nafaqat chaqira olamiz, balki ularni obyektlar sifatida korib chiqishimiz mumkin: xususiyatlarni qoshish/olib tashlash, malumotnoma orqali otish va hk.

    name xususiyati

    Funktsiya obyektlari bir nechta ishlatilishi mumkin bolgan xususiyatlarni oz ichiga oladi.

    Masalan, funktsiya nomiga name xususiyati sifatida kirish mumkin:

    function sayHi() {
      alert("Salom");
    }
    
    alert(sayHi.name); // sayHi

    Qizigi shundaki, nom berish mantiqi juda aqlli. Shuningdek, tayinlashda ishlatiladigan funktsiyalarga togri nom beriladi:

    let sayHi = function() {
      alert("Salom");
    }
    
    alert(sayHi.name); // sayHi (ishlaydi!)

    Agar tayinlash oldingan berilgan qiymat orqali bajarilgan bolsa, u ham ishlaydi:

    function f(sayHi = function() {}) {
      alert(sayHi.name); // sayHi (ishlaydi!)
    }
    
    f();

    Spetsifikatsiyada ushbu xususiyat kontekstual ism deb nomlanadi. Agar funktsiya uni taminlamasa, topshiriqda u kontekstdan aniqlanadi.

    Obyekt usullari ham nomlarga ega:

    let user = {
    
      sayHi() {
        // ...
      },
    
      sayBye: function() {
        // ...
      }
    
    }
    
    alert(user.sayHi.name); // sayHi
    alert(user.sayBye.name); // sayBye

    Bunda hech sehr yoq. Togri ismni aniqlashning imkoni bolmagan holatlar ham mavjud. Bunday holda, ism xususiyati bosh, masalan:

    // massiv ichida yaratilgan funktsiya
    let arr = [function() {}];
    
    alert( arr[0].name ); // <bo'sh matn>
    // interpretatorda to'g'ri nomni o'rnatish imkoniyati yo'q, shuning uchun bo'sh

    Amalda esa aksariyat funktsiyalarning nomi bor.

    length xususiyati

    Funktsiya parametrlari sonini qaytaradigan yana bir length xususiyati mavjud, masalan:

    function f1(a) {}
    function f2(a, b) {}
    function many(a, b, ...more) {}
    
    alert(f1.length); // 1
    alert(f2.length); // 2
    alert(many.length); // 2

    Bu erda dam qoldiq parametrlari hisoblanmaganligini korishimiz mumkin.

    length xususiyati bazan boshqa funktsiyalarda ishlaydigan funktsiyalarni introspektsiya qilish uchun ishlatiladi.

    Masalan, quyida joylashgan kodda ask funktsiyasi soraladigan savolni va chaqiruv qilish uchun tasodifiy sonlarni qabul qiladi.

    Agar foydalanuvchi oz javobini taqdim qilsa, funktsiya ishlovchilarni chaqiradi. Ikki xil ishlov beruvchidan otishimiz mumkin:

    • Nol-argumentli funktsiya, bu faqat foydalanuvchi ijobiy javob berganida chaqiriladi.
    • Ikkala holatda ham chaqiriladigan va javobni qaytaradigan argumentlarga ega funktsiya.

    Ushbu goya shundaki, bizda ijobiy holatlar uchun argumentlarsiz ishlov beruvchi sintaksis mavjud (eng tez-tez uchraydigan variant), lekin universal ishlovchilarni ham taqdim etishga qodir.

    handlers ni togri chaqirish uchun biz length xususiyatini korib chiqamiz:

    function ask(question, ...handlers) {
      let isYes = confirm(question);
    
      for(let handler of handlers) {
        if (handler.length == 0) {
          if (isYes) handler();
        } else {
          handler(isYes);
        }
      }
    
    }
    
    // ijobiy javob uchun ikkala ishlovchilar ham chaqiriladi
    // salbiy javob uchun faqat ikkinchisi
    ask("Savol?", () => alert('Siz ha dedingiz'), result => alert(result));

    Bu polimorfizm deb nomlangan alohida holat argumentlarni turiga qarab turlicha muomala qilish, yoki bizda length ga qarab. Ushbu goya JavaScript-ni kutubxonalarida ishlatilishi mumkin.

    Maxsus xususiyatlar

    Biz ozimizga xos xususiyatlarni ham qoshishimiz mumkin.

    Bu erda biz chaqiruvlarning umumiy sonini kuzatib borish uchun counter xususiyatini qoshamiz:

    function sayHi() {
      alert("Hi");
    
      // keling, necha marta bajarilganini hisoblaymiz
      sayHi.counter++;
    }
    sayHi.counter = 0; // boshlang'ich qiymati
    
    sayHi(); // Hi
    sayHi(); // Hi
    
    alert( `Called ${sayHi.counter} times` ); // 2 marta chaqirildi
    Xususiyat ozgaruvchan emas

    sayHi.counter = 0 kabi funktsiyaga tayinlangan xususiyat ichida counter mahalliy ozgaruvchanni aniqlamaydi. Boshqacha qilib aytganda, xususiyat counter va let counter bu ozaro bogliq bolmagan ikkita narsa.

    Biz funktsiyani obyekt sifatida korib chiqishimiz, unda xususiyatlarni saqlashimiz mumkin, ammo bu uning bajarilishiga tasir qilmaydi. Ozgaruvchanlar hech qachon funktsiya xususiyatlaridan foydalanmaydi va aksincha. Bu shunchaki parallel olamlar.

    Funktsiya xususiyatlari bazida yopilishni ozgartirishi mumkin. Masalan, funktsiya xususiyatidan foydalanish uchun hisoblagich funktsiyasi misolini O'zgaruvchi doirasi, yopilish (closure) bobidan qayta yozishimiz mumkin:

    function makeCounter() {
      // instead of:
      // let count = 0
    
      function counter() {
        return counter.count++;
      };
    
      counter.count = 0;
    
      return counter;
    }
    
    let counter = makeCounter();
    alert( counter() ); // 0
    alert( counter() ); // 1

    Endi counter tashqi leksik muhitida emas, balki togridan-togri funktsiyalarda saqlanadi.

    Yopishni ishlatishdan kora yaxshiroqmi yoki yomonmi?

    Asosiy farq shundaki, agar count qiymati tashqi ozgaruvchanda yashasa, tashqi kod unga kira olmaydi. Faqatgina ichki funktsiyalar uni ozgartirishi mumkin. Agar u funktsiyaga bogliq bolsa, unda bunday narsa bolishi mumkin:

    function makeCounter() {
    
      function counter() {
        return counter.count++;
      };
    
      counter.count = 0;
    
      return counter;
    }
    
    let counter = makeCounter();
    
    counter.count = 10;
    alert( counter() ); // 10

    Shunday qilib, amalga oshirishni tanlash bizning maqsadlarimizga bogliq.

    Named Function Expression

    Named Function Expression, yoki NFE, nomi bolgan Funktsiya ifodalari uchun atama.

    Masalan, oddiy funktsiya ifodasini olaylik:

    let sayHi = function(who) {
      alert(`Salom, ${who}`);
    };

    Va unga ism qoshaylik:

    let sayHi = function func(who) {
      alert(`Salom, ${who}`);
    };

    Bu yerda biror narsaga erishdikmi? Ushbu qoshimcha "func" nomining maqsadi nima?

    Birinchidan, bizda hali ham funktsiya ifodasi borligini takidlaymiz. function dan keyin "func" ismining qoshilishi uni funktsiya deklaratsiyasiga aylantirmadi, chunki u hali ham tayinlash ifodasining bir qismi sifatida yaratilgan.

    Bunday nomni qoshish hech narsani buzmadi.

    Funktsiya hanuzgacha sayHi() sifatida mavjud:

    let sayHi = function func(who) {
      alert(`Salom, ${who}`);
    };
    
    sayHi("John"); // Salom, John

    func nomi haqida ikkita alohida narsa mavjud:

    1. Bu funktsiya oziga havola qilishiga imkon beradi.
    2. Bu funksiya tashqaridan korinmaydi.

    Masalan, quyida joylashgan sayHi funktsiyasi yana ozini "Mehmon" bilan chaqiradi, agar chaqirmasa, who taqdim etiladi:

    let sayHi = function func(who) {
      if (who) {
        alert(`Salom, ${who}`);
      } else {
        func("Mehmon"); // o'zini qayta chaqirish uchun func-dan foydalaning
      }
    };
    
    sayHi(); // Salom, Guest
    
    // Ammo bu ishlamaydi:
    func(); // Error, func is not defined (funktsiya tashqaridan ko'rinmaydi)

    Nima uchun func dan foydalandik? Ehtimol, ichki chaqiruv uchun sayHi dan foydalanamiz?

    Darhaqiqat, aksariyat hollarda biz:

    let sayHi = function(who) {
      if (who) {
        alert(`Salom, ${who}`);
      } else {
        sayHi("Mehmon");
      }
    };

    Ushbu kod bilan bogliq muammo shundaki, sayHi qiymati ozgarishi mumkin. Funktsiya boshqa ozgaruvchanga otishi mumkin va kod xatolarga yol qoyishni boshlaydi:

    let sayHi = function(who) {
      if (who) {
        alert(`Hello, ${who}`);
      } else {
        sayHi("Mehmon"); // Error: sayHi is not a function
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Error, the nested sayHi call doesn't work any more!

    Buning sababi shundaki, funktsiya sayHi ni tashqi leksik muhitdan oladi. Mahalliy sayHi yoq, shuning uchun tashqi ozgaruvchandan foydalaniladi. Chaqiruv paytida tashqi sayHi null.

    Funktsiya ifodasiga kiritishimiz mumkin bolgan ixtiyoriy nom aynan shu turdagi muammolarni hal qilish uchun moljallangan.

    Kodimizni tuzatish uchun undan foydalanamiz:

    let sayHi = function func(who) {
      if (who) {
        alert(`Salom, ${who}`);
      } else {
        func("Mehmon"); // Endi barchasi yaxshi
      }
    };
    
    let welcome = sayHi;
    sayHi = null;
    
    welcome(); // Salom, Mehmon (ichki chaqiruv ishlamoqda)

    Endi u ishlaydi, chunki "func" nomi funktsional-lokal hisoblanadi. U tashqaridan olinmaydi (va u yerda korinmaydi). Spetsifikatsiya har doim mavjud funktsiyaga murojaat qilishiga kafolat beradi.

    Tashqi kod hali ham sayHi yoki welcome ozgaruvchanga ega. Va func bu ichki funktsiya nomi, funktsiya ozini ozi ichkaridan chaqira oladi.

    Funktsiya deklaratsiyasi uchun bunday narsa yoq

    Bu yerda tasvirlangan ichki ism xususiyati faqat funktsiya ifodalari uchun mavjud, funktsiya deklaratsiyalari uchun emas. Funktsiya deklaratsiyalari uchun yana bitta ichki ism qoshish uchun sintaksis imkoniyati mavjud emas.

    Bazan, bizga ishonchli ichki nom kerak bolganda, funktsiya deklaratsiyasini nomlangan funktsiya ifodasi shakliga qayta yozish uchun sabab boladi.

    Xulosa

    Funktsiyalar obyektlardir.

    Bu yerda biz ularning xususiyatlarini korib chiqdik:

    • name funktsiya nomi. Nafaqat funktsiya tarifida berilganida, balki tayinlashlar va obyekt xususiyatlari uchun ham mavjud.
    • length funktsiya tarifidagi argumentlar soni. Qoldiq parametrlari hisoblanmaydi.

    Agar funktsiya funktsiya ifodasi deb elon qilingan bolsa (asosiy kod oqimida emas) va u nomni olib yuradigan bolsa, u holda Named Function Expression deyiladi. Ism ichida rekursiv chaqiruvlar yoki shunga oxshash malumotlarga murojaat qilish uchun ishlatilishi mumkin.

    Shuningdek, funktsiyalar qoshimcha xususiyatlarga ega bolishi mumkin. Kopgina taniqli JavaScript kutubxonalari ushbu xususiyatdan juda yaxshi foydalanadi.

    Ular asosiy funktsiyani yaratadilar va unga boshqa koplab yordamchi funktsiyalarni biriktiradilar. Masalan, jquery kutubxonasi $ nomli funktsiyani yaratadi. Lodash kutubxonasi _ funktsiyasini yaratadi. Va keyin ularga _.clone, _.keyBy va boshqa xususiyatlarni qoshib qoyadilar (ular haqida koproq malumotga ega bolishni xohlagan vaqtingizda docs ga qarang). Aslida, ular buni global makonning ifloslanishini kamaytirish uchun qilishadi, shuning uchun bitta kutubxona faqat bitta global ozgaruvchanni beradi. Bu nizolarni nomlash imkoniyatini kamaytiradi.

    Shunday qilib, funktsiya nafaqat oz-ozidan biror narsa qila oladi, balki uning xususiyatlari orqali foydali funksiyalarni ham taminlaydi.

    Vazifalar

    Hisoblagich ham kamayishi va raqamni ornatishi uchun makeCounter() kodini ozgartiring:

    • counter() keyingi raqamni qaytarishi kerak (avvalgidek).
    • counter.set(value) count ni value ga ornatishi kerak.
    • counter.decrease() count ni 1 ga kamaytirishi kerak.

    Toliq foydalanish namunasi uchun sandbox kodini koring.

    P.S. Joriy hisobni ushlab turish uchun siz yopilish yoki funktsiya xususiyatidan foydalanishingiz mumkin. Yoki ikkala variantni ham yozing.

    Sinovlar bilan sandbox-ni oching.

    yechim

    Yechim mahalliy ozgaruvchan count dan foydalanadi, ammo qoshimcha usullar counter ga yoziladi. Ular bir xil tashqi leksik muhitga ega va shuningdek, hozirgi count ga kirishlari mumkin.

    function makeCounter() {
      let count = 0;
    
      function counter() {
        return count++;
      }
    
      counter.set = (value) => (count = value);
    
      counter.decrease = () => count--;
    
      return counter;
    }

    Yechimni sandbox-dagi sinovlar bilan oching.

    Quyidagi kabi ishlaydigan sum funktsiyasini yozing:

    sum(1)(2) == 3; // 1 + 2
    sum(1)(2)(3) == 6; // 1 + 2 + 3
    sum(5)(-1)(2) == 6;
    sum(6)(-1)(-2)(-3) == 0;
    sum(0)(1)(2)(3)(4)(5) == 15;

    P.S. Eslatma: funktsiyangiz uchun maxsus moslamani ibtidoiy konvertatsiyaga ornatishingiz kerak bolishi mumkin.

    Sinovlar bilan sandbox-ni oching.

    yechim
    1. Hammasi qanday bolishidan qatiy nazar ishlashi uchun sum natijasi funktsiya bolishi kerak.
    2. Ushbu funktsiya chaqiruvlar orasidagi joriy qiymatni xotirada saqlashi kerak.
    3. Vazifaga kora, funktsiya == ishlatilganda raqamga aylanishi kerak. Funktsiyalar obyektlardir, shuning uchun konvertatsiya Obyektlarni ibtidoiylarga aylantirish bobida tasvirlanganidek sodir boladi va biz raqamni qaytaradigan oz usulimizni taqdim etamiz

    Endi kod:

    function sum(a) {
      let currentSum = a;
    
      function f(b) {
        currentSum += b;
        return f;
      }
    
      f.toString = function () {
        return currentSum;
      };
    
      return f;
    }
    
    alert(sum(1)(2)); // 3
    alert(sum(5)(-1)(2)); // 6
    alert(sum(6)(-1)(-2)(-3)); // 0
    alert(sum(0)(1)(2)(3)(4)(5)); // 15

    Iltimos etibor bering, sum funktsiyasi aslida bir marta ishlaydi. Bu f funktsiyasini qaytaradi.

    Keyin har bir keyingi chaqiruvda f oz parametrini currentSum yigindisiga qoshadi va ozini qaytaradi.

    f ning oxirgi satrida rekursiya mavjud emas.

    Rekursiya nimaga oxshashi:

    function f(b) {
      currentSum += b;
      return f(); // <-- rekursiv chaqiruv
    }

    Va bizning holatimizda, biz faqatgina funktsiyani chaqirmasdan qaytaramiz:

    function f(b) {
      currentSum += b;
      return f; // <-- o'zini o'zi chaqirmaydi, o'zini qaytaradi
    }

    Ushbu f keyingi chaqiruvda ishlatiladi, yana ozimi qaytaradi, kerak bolganda. Keyin raqam yoki matn sifatida ishlatilganda toString currentSum qiymatini qaytaradi. Konvertatsiya qilish uchun biz bu yerda Symbol.toPrimitive yoki valueOf dan ham foydalanishimiz mumkin.

    Yechimni sandbox-dagi sinovlar bilan oching.

    O'quv qo'llanma xaritasi

    Izohlar

    izoh berishdan oldin buni o'qing
    • Agar sizda nimani yaxshilash kerakligi haqida takliflaringiz bo'lsa - iltimos, GitHub muammosini yuboring yoki izoh berish o'rniga so'rov yuboring.
    • Agar siz maqolada biror narsani tushunolmasangiz - iltimos, batafsilroq ma'lumot bering.
    • Bir nechta so'z so'zlarini kiritish uchun <code> yorlig'ini ishlating, bir nechta satrlar uchun - ularni <pre> yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page