JavaScript-ում ֆունկցիան «լեզվի կախարդական կառուցվածք» չէ, բայց արժեքի հատուկ տեսակ է:
Այն շարահյուսությունը, որ մինչ այս օգտագործել ենք, կոչվում է Function Declaration (Ֆունկցիոնալ Հռչակագիր):
function sayHi() {
alert( "Ողջույն" );
}
Կա նաև այլ շարահյուսություն ֆունկցիա ստեղծելու համար, որը կոչվում է Function Expression (Ֆունկցիոնալ Արտահայտություն):
Դա մեզ թույլ է տալիս ստեղծել նոր ֆունկցիա ցանկացած արտահայտության մեջտեղում:
Օրինակ.
let sayHi = function() {
alert( "Ողջույն" );
};
Այստեղ մենք կարող ենք տեսնել, որ sayHi
փոփոխականը ստանում է արժեք՝ նոր ֆունկցիա, որը ստեղծվել է այսպես՝ function() { alert("Ողջույն"); }
:
Քանի որ ֆունկցիայի ստեղծումը տեղի է ունենում վերագրման արտահայտության համատեքստում (=
նշանի աջ կողմում), ապա սա Function Expression (Ֆունկցիոնալ Արտահայտություն) է:
Նկատի ունեցեք, որ function
հիմնաբառից հետո չկա անվանում: Անվան բացթողումը թույլատրելի է Ֆունկցիոնալ Արտահայտությունների համար:
Այստեղ մենք անմիջապես վերագրում ենք այն փոփոխականի, ուստի այս կոդի նմուշների իմաստը նույնն է. «ստեղծել ֆունկցիա և տեղադրել այն sayHi
փոփոխականում»:
Ավելի առաջադեմ իրավիճակներում, որոնց մենք ավելի ուշ կառնչվենք, ֆունկցիան կարող է ստեղծվել և անմիջապես կանչվել կամ պլանավորվել հետագա գործարկման համար, ոչ մի տեղ չպահվել, այդպիսով մնալ անանուն:
Եկեք կրկնենք. անկախ նրանից, թե ինչպես է ստեղծվում ֆունկցիան, այն արժեք է: Վերոնշյալ երկու օրինակներն էլ sayHi
փոփոխականում պահում են ֆունկցիա:
Մենք կարող ենք նույնիսկ տպել այդ արժեքը՝ օգտագործելով alert
.
function sayHi() {
alert( "Ողջույն" );
}
*!*
alert( sayHi ); // ցուցադրում է ֆունկցիայի կոդը
*/!*
Նկատի ունեցեք, որ վերջին տողը չի գործարկում ֆունկցիան, որովհետև sayHi
-ից հետո չկան փակագծեր: Կան ծրագրավորման լեզուներ, որտեղ ֆունկցիայի անվան ցանկացած հիշատակում հանգեցնում է դրա գործարկմանը, բայց JavaScript-ը այդպիսին չէ։
JavaScript-ում ֆունկցիան արժեք է, այնպես որ մենք կարող ենք դրան վերաբերվել որպես արժեքի: Վերոնշյալ կոդը ցույց է տալիս իր տողային ներկայացումը, որը սկզբնական կոդ է:
Անշուշտ, ֆունկցիան հատուկ արժեք է, այն իմաստով, որ մենք կարող ենք այն կանչել որպես sayHi()
:
Բայց դա դեռևս արժեք է: Այսպիսով, մենք կարող ենք աշխատել դրա հետ այնպես, ինչպես այլ տեսակի արժեքների հետ:
Մենք կարող ենք պատճենել ֆունկցիան մեկ այլ փոփոխականում.
function sayHi() { // (1) ստեղծել
alert( "Ողջույն" );
}
let func = sayHi; // (2) պատճենել
func(); // Ողջույն // (3) գործարկել կրկնօրինակը (սա աշխատում է)
sayHi(); // Ողջույն // սա նույնպես դեռ աշխատում է (ինչու ոչ)
Ահա մանրամասն, թե ինչ է տեղի ունենում վերևում.
- Ֆունկցիոնալ Հռչակագիրը
(1)
ստեղծում է ֆունկցիա և տեղադրում այնsayHi
անվանումով փոփոխականում: - Տող
(2)
-ը պատճենում է այնfunc
փոփոխականում: Կրկին նկատի ունեցեք.sayHi
-ից հետո չկան փակագծեր: Եթե լինեին, ապաfunc = sayHi()
կվերագրերsayHi()
-ի կանչվելու արդյունքըfunc
-ին, այլ ոչ հենցsayHi
ֆունկցիան: - Այժմ երկու եղանակով էլ ֆունկցիան կարող է կանչվել՝
sayHi()
ևfunc()
:
Մենք sayHi
հռչակելու համար կարող էինք օգտագործել նաև Ֆունկցիոնալ Արտահայտություն առաջին տողում.
let sayHi = function() { // (1) ստեղծել
alert( "Ողջույն" );
};
let func = sayHi; //(2)
// ...
Ամեն ինչ նույն կերպ կաշխատի:
Կարող եք զարմանալ, թե ինչո՞ւ Ֆունկցիոնալ Արտահայտություններն ունեն կետ-ստորակետ `;` վերջում, իսկ Ֆունկցիոնալ Հռչակագրերը չունեն.
```js
function sayHi() {
// ...
}
let sayHi = function() {
// ...
}*!*;*/!*
```
Պատասխանը պարզ է. Ֆունկցիոնալ Արտահայտությունն այստեղ որպես `function(…) {…}` ստեղծվել է վերագրման դրույթի ներսում. `let sayHi = …;` (statement): Կետ-ստորակետը `;` խորհուրդ է տրվում դնել դրույթի վերջում, դա ֆունկցիայի շարահյուսության մաս չէ:
Կետ-ստորակետը կարող է լինել այդտեղ ավելի պարզ վերագրման համար, ինչպիսին է `let sayHi = 5;`, այն այդտեղ է նաև ֆունկցիա վերագրելու համար:
Դիտարկենք ֆունկցիաները որպես արժեքներ փոխանցելու և ֆունկցիոնալ արտահայտություններ օգտագործելու ավելի շատ օրինակներ:
Մենք կգրենք ֆունկցիա ask(question, yes, no)
երեք պարամետրերով:
question
: Հարցի տեքստը
yes
: Գործարկման համար ֆունկցիա, եթե պատասխանը «Դրական» է
no
: Գործարկման համար ֆունկցիա, եթե պատասխանը «Բացասական» է
Ֆունկցիան պետք է հարց տա question
այնուհետև, կախված օգտատիրոջ պատասխանից, կանչի yes()
կամ no()
.
*!*
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
*/!*
function showOk() {
alert( "Դուք համաձայնեցիք:" );
}
function showCancel() {
alert( "Դուք չեղարկեցիք գործարկումը։" );
}
// օգտագործումը. showOk և showCancel ֆունկցիաները փոխանցվում են ask ֆունկցիային որպես արգումենտներ
ask("Դուք համաձա՞յն եք:", showOk, showCancel);
Գործնականում նման ֆունկցիաները բավականին օգտակար են: Հիմնական տարբերությունը «իրական կյանքով» ask
-ի և վերոնշյալ օրինակի միջև այն է, որ «իրական կյանքով» ֆունկցիաները օգտագործում են ավելի բարդ ուղիներ օգտատիրոջ հետ փոխազդելու համար, քան պարզ confirm
-ը: Բրաուզերում նման ֆունկցիաները սովորաբար նկարում են գեղեցիկ տեսք ունեցող հարցերի պատուհան: Բայց դա այլ պատմություն է:
ask
ֆունկցիայի showOk
և showCancel
արգումենտները կոչվում են callback functions (հետկանչ ֆունկցիաներ) կամ պարզապես callbacks (հետկանչեր):
Գաղափարն այն է, որ մենք փոխանցում ենք ֆունկցիա և ակնկալում, որ այն հետագայում «հետ կկանչվի» անհրաժեշտության դեպքում: Մեր պարագայում showOk
-ը դառնում է հետկանչ «դրական» պատասխանի համար, իսկ showCancel
-ը՝ «բացասական» պատասխանի համար:
Մենք կարող ենք օգտագործել Ֆունկցիոնալ Արտահայտություններ՝ նույն ֆունկցիան ավելի հակիրճ գրելու համար.
function ask(question, yes, no) {
if (confirm(question)) yes()
else no();
}
*!*
ask(
"Դուք համաձա՞յն եք:",
function() { alert("Դուք համաձայնեցիք:"); },
function() { alert("Դուք չեղարկեցիք գործարկումը։"); }
);
*/!*
Այստեղ ֆունկցիաները ճշգրիտ են հռչակված ask(...)
կանչի ներսում: Նրանք չունեն անվանում, ուստի կոչվում են anonymous (անանուն): Նման ֆունկցիաները հասանելի չեն ask
-ից դուրս (քանի որ դրանք վերագրված չեն փոփոխականների), բայց դա հենց այն է, ինչ մենք ուզում ենք այստեղ:
Շատ բնական է, որ նմանատիպ կոդ է հայտնվում մեր սքրիփթներում, այն JavaScript-ի ոգով է:
Սովորական արժեքները, ինչպիսիք են տողերը կամ թվերը, ներկայացնում են *տվյալներ*:
Ֆունկցիան կարող է ընկալվել որպես *գործողություն*։
Մենք կարող ենք այն փոխանցել փոփոխականների միջև և գործարկել` երբ կցանկանանք:
Եկեք ձևակերպենք Ֆունկցիոնալ Հռչակագրի և Արտահայտության միջև հիմնական տարբերությունները:
Առաջինը՝ շարահյությություն. ինչպես տարանջատել դրանք կոդում:
-
Ֆունկցիոնալ Հռչակագիր (Function Declaration). ֆունկցիա՝ հայտարարված որպես առանձին դրույթ (statement) հիմնական կոդի հոսքում:
// Ֆունկցիոնալ Հռչակագիր function sum(a, b) { return a + b; }
-
Ֆունկցիոնալ Արտահայտություն (Function Expression). ֆունկցիա՝ ստեղծված արտահայտության կամ մեկ այլ շարահյուսական կառուցվածքի ներսում: Այստեղ ֆունկցիան ստեղծվում է «վերագրման արտահայտության»
=
աջ կողմում.// Ֆունկցիոնալ Արտահայտություն let sum = function(a, b) { return a + b; };
Ավելի նուրբ տարբերությունն այն է, թե երբ է ֆունկցիան ստեղծվում JavaScript շարժիչի կողմից:
Ֆունկցիոնալ Արտահայտությունը ստեղծվում է, երբ կատարումը հասնում է դրան և կիրառելի է միայն այդ պահից:
Երբ կատարման հոսքն անցնում է վերագրման աջ կողմ let sum = function…
, այդ պահից սկսած ֆունկցիան համարվում է ստեղծված և կարող է օգտագործվել (վերագրվել, կանչվել, և այլն․․․):
Ֆունկցիոնալ Հռչակագրերը տարբեր են:
Ֆունկցիոնալ Հռչակագիրը կարող է կանչվել ավելի վաղ, քան այն սահմանվել է:
Օրինակ՝ գլոբալ Ֆունկցիոնալ Հռչակագիրը հասանելի է ամբողջ սքրիփթում, անկախ նրանից, թե որտեղ է այն գտնվում:
Դա պայմանավորված է ներքին ալգորիթմներով: Երբ JavaScript-ը պատրաստվում է գործարկել սքրիփթը, այն նախ որոնում է գլոբալ Ֆունկցիոնալ Հռչակագրեր դրանում և ստեղծում ֆունկցիաներ: Մենք կարող ենք դա համարել որպես «նախաձեռնման փուլ»:
ԵՎ այն բանից հետո, երբ բոլոր Ֆունկցիոնալ Հռչակագրերը մշակվել են, կոդը կատարվում է: Այսպիսով, այն ունի հասանելիություն այս ֆունկցիաներին:
Օրինակի համար սա աշխատում է.
*!*
sayHi("Պողոս"); // Ողջույն Պողոս
*/!*
function sayHi(name) {
alert( `Ողջույն ${name}` );
}
Ֆունկցիոնալ Հռչակագիրը sayHi
ստեղծվում է, երբ JavaScript-ը պատրաստվում է սկսել սքրիփթը և դրա մեջ ամենուր տեսանելի է այդ ֆունկցիան:
...Եթե դա լիներ Ֆունկցիոնալ Արտահայտություն, ապա այն չէր աշխատի.
*!*
sayHi("Պողոս"); // սխալ
*/!*
let sayHi = function(name) { // (*) այլևս ոչ մի կախարդանք
alert( `Ողջույն ${name}` );
};
Ֆունկցիոնալ Արտահայտությունները ստեղծվում են, երբ կատարումը հասնում է դրանց: Դա տեղի կունենա միայն (*)
տողում։ Չափազանց ուշ։
Ֆունկցիոնալ Արտահայտությունների մեկ այլ առանձնահատկություն է դրանց բլոկային տեսադաշտը։
Խիստ ռեժիմում, երբ Ֆունկցիոնալ Հռչակագիրը գտնվում է կոդի բլոկի ներսում, այն տեսանելի է ամենուր՝ այդ բլոկի ներսում, բայց ոչ՝ դրանից դուրս:
Օրինակի համար եկեք պատկերացնենք, որ մենք պետք է հռչակենք welcome()
ֆունկցիան՝ կախված age
փոփոխականից, որը մենք ստանում ենք գործարկման ընթացքում: Եվ հետո մենք պլանավորում ենք օգտագործել այն որոշ ժամանակ անց:
Եթե մենք օգտագործում ենք Ֆունկցիոնալ Հռչակագիր, այն չի աշխատի այնպես, ինչպես նախատեսված էր.
let age = prompt("Քանի՞ տարեկան եք:", 18);
// պայմանականորեն հռչակել ֆունկցիա
if (age < 18) {
function welcome() {
alert("Ողջույն");
}
} else {
function welcome() {
alert("Ողջույններ");
}
}
// ...օգտագործել ավելի ուշ
*!*
welcome(); // սխալ` welcome is not defined
*/!*
Դա պայմանավորված է նրանով, որ Ֆունկցիոնալ Հռչակագիրը տեսանելի է միայն այն կոդի բլոկի ներսում, որտեղ այն գտնվում է:
Ահա ևս մեկ օրինակ.
let age = 16; // օրինակի համար վերցրեք 16
if (age < 18) {
*!*
welcome(); // \ (գործարկվում է)
*/!*
// |
function welcome() { // |
alert("Ողջույն"); // | Ֆունկցիոնալ Հռչակագիրը հասանելի է
} // | ամենուր՝ բլոկում, որտեղ այն հայտարարված է
// |
*!*
welcome(); // / (գործարկվում է)
*/!*
} else {
function welcome() {
alert("Ողջույններ");
}
}
// Այստեղ մենք ձևավոր փակագծերից դուրս ենք,
// այնպես որ մենք չենք կարող տեսնել դրանց ներսում ստեղծված Ֆունկցիոնալ Հռչակագրերը:
*!*
welcome(); // սխալ՝ welcome is not defined
*/!*
Ի՞նչ կարող ենք անել, որպեսզի welcome
-ը դարձնենք տեսանելի if
-ից դուրս:
Ճիշտ մոտեցումը կլինի՝ օգտագործել Ֆունկցիոնալ Արտահայտություն և վերագրել welcome
-ը փոփոխականի, որը հռչակված է if
-ից դուրս և ունի համապատասխան տեսանելիություն:
Այս կոդը աշխատում է այնպես, ինչպես նախատեսված է.
let age = prompt("Քանի՞ տարեկան եք:", 18);
let welcome;
if (age < 18) {
welcome = function() {
alert("Ողջույն");
};
} else {
welcome = function() {
alert("Ողջույններ");
};
}
*!*
welcome(); // այժմ նորմալ է
*/!*
Կամ մենք կարող ենք այն էլ ավելի պարզեցնել՝ օգտագործելով ?
հարցական նշանի օպերատոր.
let age = prompt("Քանի՞ տարեկան եք:", 18);
let welcome = (age < 18) ?
function() { alert("Ողջույն"); } :
function() { alert("Ողջույններ"); };
*!*
welcome(); // այժմ նորմալ է
*/!*
Որպես հիմնական կանոն, երբ մենք պետք է հայտարարենք ֆունկցիա, առաջին հերթին պետք է հաշվի առնել Ֆունկցիոնալ Հռչակագրի շարահուսությունը: Այն ավելի շատ ազատություն է տալիս այն հարցում, թի ինչպես կազմակերպենք մեր կոդը, քանի որ մենք կարող ենք կանչել այդպիսի ֆունկցիաները նախքան նրանց հռչակումը:
Դա նաև ավելի ընթեռնելի է, քանի որ ավելի հեշտ է փնտրել `function f(…) {…}` կոդում, քան՝ `let f = function(…) {…};`: Ֆունկցիոնալ Հռչակագրերն ավելի «գրավիչ են աչքի համար»:
...Բայց եթե Ֆունկցիոնալ Հռչակագիրն ինչ-ինչ պատճառներով մեզ չի համապատասխանում, կամ մեզ անհրաժեշտ է պայմանական հայտարարագիր (մենք հենց նոր տեսանք օրինակը), ապա պետք է օգտագործվի Ֆունկցիոնալ Արտահայտություն:
- Ֆունկցիաներն արժեքներ են: Դրանք կարող են վերագրվել, պատճենվել կամ հռչակվել կոդի ցանկացած վայրում:
- Եթե հիմնական կոդի հոսքում ֆունկցիան հռչակվում է որպես առանձին դրույթ, դա կոչվում է «Ֆունկցիոնալ Հռչակագիր» (Function Declaration):
- Եթե ֆունկցիան ստեղծվում է որպես արտահայտության մաս, այն կոչվում է «Ֆունկցիոնալ Արտահայտություն» (Function Expression):
- Ֆունկցիոնալ Հռչակագրերը մշակվում են նախքան կոդի բլոկի գործարկումը: Դրանք բլոկում տեսանելի են ամենուր:
- Ֆունկցիոնալ Արտահայտությունները ստեղծվում են, երբ կատարողական հոսքը հասնում է դրանց:
Շատ դեպքերում, երբ մենք պետք է հռչակենք ֆունկցիա, գերադասելի է Ֆունկցիոնալ Հռչակագիր, քանի որ այն տեսանելի է նախքան իր հռչակումը: Դա մեզ ավելի շատ ճկունություն է տալիս կոդի կազմակերպման հարցում և սովորաբար ավելի ընթեռնելի է:
Այսպիսով, մենք պետք է օգտագործենք Ֆունկցիոնալ Արտահայտություն միայն այն դեպքում, երբ Ֆունկցիոնալ Հռչակագիրը հարմար չէ առաջադրանքի համար: Մենք տեսել ենք դրա մի քանի օրինակ այս գլխում և ավելին կտեսնենք հետագայում: