JavaScript функцууд. Илэрхий JavaScript: Функцээс олон утгыг буцаадаг Javascript функцууд

Хүмүүс компьютерийн шинжлэх ухааныг суут хүмүүст зориулсан урлаг гэж боддог. Бодит байдал дээр энэ нь эсрэгээрээ - зүгээр л олон хүмүүс бие биенийхээ дээр зогсох зүйлсийг хийж, жижиг хайрга чулуугаар хана хийж байгаа мэт.

Дональд Кнут

Та аль хэдийн alert гэх мэт функцүүдийн дуудлагыг харсан. Функцууд нь JavaScript програмчлалын талх, цөцгийн тос юм. Хөтөлбөрийн хэсгийг боож, хувьсагч гэж нэрлэх санаа маш их алдартай. Энэ нь том программуудыг бүтэцжүүлэх, давталтыг багасгах, дэд программуудыг нэрлэх, дэд программуудыг бие биенээсээ тусгаарлах хэрэгсэл юм.

Функцийн хамгийн тод хэрэглээ бол шинэ толь бичиг үүсгэх явдал юм. Энгийн хүний ​​зохиолд үг зохиох нь муу хэлбэр. Энэ нь програмчлалын хэлэнд зайлшгүй шаардлагатай.

Дундаж насанд хүрсэн орос хэлтэй хүн 10,000 орчим үг мэддэг. Ховор програмчлалын хэл нь 10,000 суулгасан тушаалуудыг агуулдаг. Мөн програмчлалын хэлний үгсийн сан нь илүү тодорхой тодорхойлогддог тул хүнийхээс бага уян хатан байдаг. Тиймээс, шаардлагагүй давтахаас зайлсхийхийн тулд бид ихэвчлэн өөрсдийн үгээ нэмэх хэрэгтэй болдог.

Функцийн тодорхойлолт Функцийн тодорхойлолт нь ердийн хувьсагчийн тодорхойлолт бөгөөд хувьсагчийн хүлээн авах утга нь функц юм. Жишээлбэл, дараах код нь өгөгдсөн тооны квадратыг тооцоолох функцэд хамаарах хувьсагчийн квадратыг тодорхойлдог.

Var квадрат = функц(x) ( буцах x * x; ); console.log(дөрвөлжин(12)); // → 144

Функцийг функц түлхүүр үгээр эхэлсэн илэрхийллээр үүсгэнэ. Функцууд нь параметрийн багцтай (энэ тохиолдолд зөвхөн x), функцийг дуудах үед гүйцэтгэх ёстой зааврыг агуулсан биетэй. Функцийн бие нь нэг хэллэгээс бүрдсэн байсан ч үргэлж буржгар хаалтанд хадгалагддаг.

Функц нь хэд хэдэн параметртэй эсвэл огт байхгүй байж болно. Дараах жишээнд makeNoise-д параметрийн жагсаалт байхгүй, харин чадал нь хоёр байна:

Var makeNoise = function() ( console.log("Новш!"); ); дуу чимээ гаргах(); // → Хря! var power = функц(суурь, илтгэгч) ( var үр дүн = 1; for (var тоо = 0; тоолох)< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

Зарим функц нь хүч, квадрат гэх мэт утгыг буцаадаг бол зарим нь makeNoise гэх мэт утгыг буцаадаггүй бөгөөд энэ нь зөвхөн гаж нөлөө үүсгэдэг. Буцах мэдэгдэл нь функцийн буцаасан утгыг тодорхойлдог. Програмын боловсруулалт энэ зааварт хүрэхэд тэр даруй функцээс гарч, энэ утгыг тухайн функцийг дуудсан код руу буцаана. илэрхийлэлгүй буцах нь тодорхойгүй буцаана.

Параметр ба хамрах хүрээ Функцийн параметрүүд нь ижил хувьсагчид боловч тэдгээрийн анхны утгыг функцийг дуудах үед тохируулдаг бөгөөд кодонд нь биш.

Функцийн чухал шинж чанар нь функц дотор үүсгэгдсэн хувьсагч (параметрүүдийг оруулаад) тухайн функцэд локал байх явдал юм. Энэ нь чадлын жишээн дээр функцийг дуудах бүрт үр дүнгийн хувьсагч үүсэх бөгөөд эдгээр бие даасан хувилгаанууд нь хоорондоо ямар ч холбоогүй болно гэсэн үг юм.

Энэ хувьсагчийн байршил нь зөвхөн функц дотор үүсгэгдсэн параметрүүд болон хувьсагчид хамаарна. Аливаа функцээс гадуур тодорхойлогдсон хувьсагчдыг программ даяар харагдах тул глобал гэж нэрлэдэг. Хэрэв та ижил нэртэй локал хувьсагчийг зарлаагүй бол функц доторх ийм хувьсагчдад хандах боломжтой.

Дараах код нь үүнийг харуулж байна. Энэ нь x хувьсагчид утга оноох хоёр функцийг тодорхойлж дууддаг. Эхнийх нь үүнийг локал гэж зарлаж, улмаар зөвхөн орон нутгийн хувьсагчийг өөрчилдөг. Хоёр дахь нь зарладаггүй тул функц доторх x-тэй ажиллах нь жишээний эхэнд тодорхойлсон x глобал хувьсагчийг хэлнэ.

Var x = "гадна"; var f1 = function() ( var x = "f1 дотор"; ); f1(); console.log(x); // → гадна var f2 = function() ( x = "f2 дотор"; ); f2(); console.log(x); // → f2 дотор

Энэ зан үйл нь функцүүдийн хоорондын санамсаргүй харилцан үйлчлэлээс урьдчилан сэргийлэхэд тусалдаг. Хэрэв бүх хувьсагчийг програмын аль ч хэсэгт ашигласан бол нэг хувьсагчийг өөр өөр зорилгоор ашиглахгүй байх нь маш хэцүү байх болно. Хэрэв та хувьсагчийг дахин ашиглах юм бол гуравдагч талын код таны хувьсагчийн утгыг эвдэх үед хачирхалтай үр дагавар гарах болно. Функц-локал хувьсагчдыг зөвхөн функц дотор байхаар авч үзсэнээр уг хэл нь функцүүдтэй тусдаа жижиг орчлон ертөнц мэт ажиллах боломжтой болж, бүхэл бүтэн кодын талаар санаа зовохгүй байх боломжийг олгоно.

Nested scoping JavaScript нь зөвхөн глобал болон локал хувьсагчдыг ялгахаас илүүтэй. Функцуудыг функц дотор тодорхойлж болох бөгөөд үүний үр дүнд олон түвшний орон нутгийн байдал бий болно.

Жишээлбэл, дараах нэлээд утгагүй функц дотор хоёр өөр функцийг агуулна.

Var ландшафт = функц() ( var үр дүн = ""; var хавтгай = функц(хэмжээ) ( for (var тоо = 0; тоолох)< size; count++) result += "_"; }; var mountain = function(size) { result += "/"; for (var count = 0; count < size; count++) result += """; result += "\\"; }; flat(3); mountain(4); flat(6); mountain(1); flat(1); return result; }; console.log(landscape()); // → ___/""""\______/"\_

Хавтгай болон уулын функцууд нь үр дүнгийн хувьсагчийг хардаг, учир нь тэдгээр нь түүнийг тодорхойлсон функц дотор байдаг. Гэхдээ нэг функцийн хувьсагч нь нөгөө функцийн хамрах хүрээнээс гадуур байдаг тул тэд бие биенийхээ тоолох хувьсагчдыг харж чадахгүй. Мөн ландшафтын функцын гаднах орчин нь энэ функц дотор тодорхойлсон хувьсагчдыг харахгүй байна.

Товчхондоо, орон нутгийн хамрах хүрээ бүрд та үүнийг агуулсан бүх хүрээг харж болно. Функц доторх хувьсагчийн багц нь тухайн функцийг программд зарласан байршлаар тодорхойлогддог. Функцийн тодорхойлолтыг тойрсон блокуудын бүх хувьсагчууд харагдана, үүнд үндсэн програмын дээд түвшинд тодорхойлсон хувьсагч орно. Хамрах хүрээний энэ хандлагыг лексик гэж нэрлэдэг.

Бусад програмчлалын хэлийг судалж байсан хүмүүс буржгар хаалтанд хавсаргасан аливаа блок өөрийн гэсэн орон нутгийн орчныг бүрдүүлдэг гэж боддог. Гэхдээ JavaScript-д зөвхөн функцүүд хамрах хүрээг үүсгэдэг. Та бие даасан блокуудыг ашиглаж болно:

Var what = 1; ( var something = 2; // Ямар нэг зүйл хувьсагчаар ямар нэг зүйл хий... ) // Блокоос гарлаа...

Гэхдээ блок доторх ямар нэг зүйл гаднахтай ижил хувьсагч юм. Хэдийгээр ийм блокуудыг ашиглахыг зөвшөөрдөг боловч зөвхөн if хэллэг болон гогцоонуудыг ашиглах нь утга учиртай.

Хэрэв энэ нь танд хачирхалтай санагдаж байвал та ганцаараа тэгж боддоггүй. JavaScript 1.7 хувилбар дээр гарч ирэв түлхүүр үг let, энэ нь var шиг ажилладаг боловч зөвхөн функцээс гадна аль ч блокийн локал хувьсагчийг үүсгэдэг.

Утга болгон функцууд Функцийн нэрийг ихэвчлэн програмын нэр болгон ашигладаг. Ийм хувьсагчийг нэг удаа тохируулдаг бөгөөд өөрчлөгдөхгүй. Тиймээс функц болон түүний нэрийг төөрөлдүүлэхэд хялбар байдаг.

Гэхдээ эдгээр нь хоёр өөр зүйл юм. Функцийн дуудлагыг энгийн хувьсагч шиг ашиглаж болно - жишээлбэл, ямар ч илэрхийлэлд ашиглагддаг. Функцийн дуудлагыг шинэ хувьсагчид хадгалах, өөр функцэд параметр болгон дамжуулах гэх мэт боломжтой. Мөн функцийн дуудлагыг хадгалах хувьсагч нь ердийн хувьсагч хэвээр байх бөгөөд түүний утгыг өөрчилж болно:

Var launchMissiles = функц(утга) (missileSystem.launch("эсвэл!"); ); if (safeMode) launchMissiles = функц(утга) (/* цуцлах */);

5-р бүлэгт бид функцийн дуудлагыг бусад функцууд руу дамжуулснаар таны хийж болох гайхалтай зүйлсийн талаар ярилцах болно.

Функцуудыг зарлах “var квадрат = функц...” гэсэн илэрхийллийн богино хувилбар бий. Үйл ажиллагааны түлхүүр үгийг мэдэгдлийн эхэнд ашиглаж болно:

Функцийн квадрат(x) ( буцах x * x; )

Энэ бол функцийн мэдэгдэл юм. Уг мэдэгдэл нь квадрат хувьсагчийг тодорхойлж түүнд өгөгдсөн функцийг оноож өгдөг. Одоогоор маш сайн. Ийм тодорхойлолтод ганцхан алдаа бий.

Console.log("Ирээдүй хэлсэн байна:", ирээдүй()); function future() ( "Бидэнд нисдэг машин байхгүй хэвээр байна."; ) буцаана.

Энэ код нь функцийг ашигладаг кодын доор зарласан ч ажилладаг. Учир нь функцийн мэдэгдлүүд нь дээрээс доошоо программыг гүйцэтгэх ердийн нэг хэсэг биш юм. Тэдгээрийг хамрах хүрээний дээд хэсэгт "шилжүүлсэн" бөгөөд тухайн хамрах хүрээний дурын кодоор дуудаж болно. Заримдаа энэ нь тохиромжтой байдаг, учир нь та дээрх бүх функцийг ашиглаж байгаа газарт нь тодорхойлохоос санаа зовох шаардлагагүйгээр кодыг хамгийн утга учиртай дарааллаар бичиж болно.

Хэрэв бид нөхцөлт блок эсвэл давталт дотор функцийн мэдэгдлийг байрлуулбал юу болох вэ? Та үүнийг хийх шаардлагагүй. Түүхийн хувьд өөр өөр JavaScript ажиллаж байгаа платформууд ийм тохиолдлуудыг өөр өөрөөр зохицуулдаг байсан бөгөөд одоогийн хэлний стандарт үүнийг хориглодог. Хэрэв та програмуудаа дараалан ажиллуулахыг хүсвэл зөвхөн бусад функцууд эсвэл үндсэн програм доторх функцийн мэдэгдлийг ашиглана уу.

Функцийн жишээ() ( функц a() () // хэвийн бол (ямар нэгэн зүйл) ( функц b() () // Ай-яй-яа! ) )

Дуудлагын стек Гүйцэтгэлийн дараалал нь функцуудтай хэрхэн ажилладагийг нарийвчлан судлахад тустай. Энд энгийн програмолон функцийн дуудлагатай:

Функц greet(who) ( console.log("Сайн уу, " + who); ) greet("Семён"); console.log("Pokeda");

Үүнийг дараах байдлаар боловсруулдаг: мэндчилгээг дуудах нь дамжуулалтыг функцын эхлэл рүү шилжүүлэхэд хүргэдэг. Энэ нь суурилуулсан console.log функцийг дууддаг бөгөөд энэ нь хяналтыг таслан зогсоож, ажлаа хийж, хяналтыг буцааж өгдөг. Дараа нь тэр мэндчилгээний төгсгөлд хүрч, дуудагдсан газар руугаа буцдаг. Дараагийн мөрөнд console.log дахин дуудагдана.

Үүнийг схемийн дагуу дараах байдлаар харуулж болно.

Дээд мэндчилгээний console.log дээд мэндчилгээний дээд console.log дээд

Функц нь дуудагдсан газар руугаа буцах ёстой тул компьютер тухайн функцийг дуудсан контекстийг санах ёстой. Нэг тохиолдолд console.log буцаж мэндчилнэ. Нөгөө талаар тэрээр хөтөлбөрийн төгсгөлд буцаж ирдэг.

Компьютер контекстийг санаж байгаа газрыг стек гэж нэрлэдэг. Функцийг дуудах болгонд одоогийн контекст стекийн дээд талд шилждэг. Функц буцаж ирэхэд стекээс дээд контекстийг гаргаж ирээд үргэлжлүүлэн ажиллуулахын тулд үүнийг ашиглана.

Стек хадгалахад санах ойн зай шаардлагатай. Стек хэт томрох үед компьютер ажиллахаа больж, "стек халих" эсвэл "хэт их рекурс" гэх мэт зүйлийг хэлэх болно. Дараах код нь үүнийг харуулж байна - энэ нь компьютерээс маш нарийн төвөгтэй асуулт асуудаг бөгөөд энэ нь хоёр функцийн хооронд төгсгөлгүй үсрэлт хийхэд хүргэдэг. Бүр нарийн яривал компьютер хязгааргүй стектэй байсан бол энэ нь хязгааргүй үсрэлт болно. Бодит байдал дээр стек хальж байна.

Тахианы функц() ( өндөг буцах(); ) функц өндөг() ( буцах тахиа(); ) console.log(chicken() + " түрүүлж ирсэн."); // → ??

Нэмэлт аргументууд Дараах код нь бүрэн хууль ёсны бөгөөд ямар ч асуудалгүй ажилладаг.

Анхааруулга("Сайн уу", "Оройн мэнд", "Бүгдээрээ сайн уу!");

Албан ёсоор функц нь нэг аргумент авдаг. Гэсэн хэдий ч, ийм байдлаар сорилтод ороход тэр гомдоллодоггүй. Тэр бусад маргааныг үл тоомсорлож, "Сайн уу" гэж харуулдаг.

JavaScript нь функцэд дамжуулагдсан аргументуудын тоог маш нарийн тодорхойлдог. Хэрэв та хэт их шилжүүлбэл нэмэлтийг үл тоомсорлох болно. Хэт цөөхөн бөгөөд алга болсон хүмүүст тодорхойгүй утгыг онооно.

Энэ аргын сул тал нь функцэд буруу тооны аргументыг хэн ч гомдоллохгүйгээр дамжуулах боломжтой, тэр ч байтугай магадлал юм.

Давуу тал нь нэмэлт аргумент авах функцүүдийг үүсгэх боломжтой юм. Жишээлбэл, чадлын функцийн дараагийн хувилбарт үүнийг хоёр эсвэл нэг аргументын аль нэгээр нь дуудаж болно - сүүлчийн тохиолдолд экспонент нь хоёртой тэнцүү байх бөгөөд функц нь квадрат шиг ажилладаг.

Функцийн хүч(суурь, илтгэгч) ( хэрэв (дүр == тодорхойгүй) илтгэгч = 2; var үр дүн = 1; хувьд (var тоо = 0; тоолох)< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

Дараагийн бүлэгт бид функцийн үндсэн хэсэгт түүнд дамжуулагдсан аргументуудын тодорхой тоог хэрхэн олж болохыг олж харах болно. Энэ нь ашигтай, учир нь ... ямар ч тооны аргумент авах функцийг үүсгэх боломжийг танд олгоно. Жишээлбэл, console.log нь энэ шинж чанарыг ашигладаг бөгөөд түүнд дамжуулсан бүх аргументуудыг хэвлэдэг:

Console.log("R", 2, "D", 2); // → R 2 D 2

Хаалт Функцийн дуудлагыг хувьсагч болгон ашиглах чадвар нь функцийг дуудах бүрт локал хувьсагчдыг шинээр үүсгэдэг нь биднийг сонирхолтой асуултад хүргэдэг. Функц ажиллахаа болих үед орон нутгийн хувьсагчдад юу тохиолдох вэ?

Дараах жишээ нь энэ асуудлыг харуулж байна. Энэ нь локал хувьсагчийг үүсгэдэг wrapValue функцийг зарладаг. Дараа нь энэ локал хувьсагчийг уншиж утгыг нь буцаадаг функцийг буцаана.

WrapValue(n) функц ( var localVariable = n; return function() ( localVariable буцаах; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); console.log(wrap1()); // → 1 console.log(wrap2()); // → 2

Энэ нь хүчинтэй бөгөөд зохих ёсоор ажилладаг - хувьсагч руу хандах эрх хэвээр байна. Түүнчлэн, ижил хувьсагчийн хэд хэдэн тохиолдлууд нэгэн зэрэг байж болох бөгөөд энэ нь функцийн дуудлага болгонд локал хувьсагчдыг дахин үүсгэдэг болохыг баталж байна.

Локал хувьсагчийн жишээн дээр ажиллах энэхүү чадварыг хаалт гэж нэрлэдэг. Орон нутгийн хувьсагчдыг хаадаг функцийг хаах гэж нэрлэдэг. Энэ нь таныг хувьсах амьдралын хугацааны талаар санаа зовохоос чөлөөлж зогсохгүй функцүүдийг бүтээлчээр ашиглах боломжийг олгодог.

Бага зэрэг өөрчлөлт хийснээр бид жишээгээ тоонуудыг өгөгдсөн тоогоор үржүүлдэг функц болгон хувиргадаг.

Функцийн үржүүлэгч(фактор) ( буцах функц(тоо) ( буцах тоо * хүчин зүйл; ); ) var two = үржүүлэгч(2); console.log(хоёр удаа(5)); // → 10

wrapValue жишээнээс localVariable гэх мэт тусдаа хувьсагч хэрэггүй болсон. Параметр нь өөрөө орон нутгийн хувьсагч учраас.

Ингэж бодож эхлэхийн тулд дадлага хийх шаардлагатай болно. Сайн сонголтсэтгэцийн загвар - функц нь кодыг биедээ хөлдөөж, савлагаанд боож өгнө гэж төсөөлөөд үз дээ. Буцах функцийг(...) (...) харах үед үүнийг дараа нь ашиглах зорилгоор царцаасан кодын хяналтын самбар гэж бодоорой.

Бидний жишээн дээр үржүүлэгч нь хоёр дахин хувьсагчид хадгалсан кодын хөлдөөсөн хэсгийг буцаадаг. Сүүлийн мөр нь хувьсагчид агуулагдах функцийг дууддаг бөгөөд энэ нь хадгалагдсан кодыг идэвхжүүлэхэд хүргэдэг (буцах дугаар * хүчин зүйл;). Энэ нь үржүүлэгчийг дуудах үед тодорхойлсон хүчин зүйлийн хувьсагч руу хандах эрхтэй хэвээр байгаа бөгөөд гэсгээх (5) үед дамжуулсан аргументыг тоон параметр болгон ашиглах боломжтой.

Дахин давтагдах Функц нь стекийг дүүргэхээс сэргийлж өөрийгөө дуудаж болно. Энэ функцийг рекурсив гэж нэрлэдэг. Экспоненциацийн өөр хувилбарын жишээ энд байна.

Функцийн хүч(суурь, илтгэгч) ( хэрэв (дүр == 0) буцаана 1; else буцаана суурь * хүч(суурь, экспонент - 1); ) console.log(power(2, 3)); // → 8

Математикчид экспоненциацийг ойролцоогоор ингэж тодорхойлдог бөгөөд энэ нь мөчлөг гэхээсээ илүү дэгжин ойлголтыг дүрсэлсэн байж магадгүй юм. Функц нь олон үржүүлгийн үр дүнд хүрэхийн тулд өөр өөр аргументуудаар өөрийгөө олон удаа дууддаг.

Гэсэн хэдий ч, энэ хэрэгжилтэд асуудал байна: хэвийн орчин JavaScript нь гогцоотой хувилбараас 10 дахин удаан. Гогцоо дундуур алхах нь функц дуудахаас хамаагүй хямд юм.

Хурд ба дэгжин байдлын асуудал нэлээд сонирхолтой юм. Хүний тав тухтай байдал, машинд тав тухтай байдал хоёрын хооронд тодорхой ялгаа бий. Аливаа программыг илүү том, төвөгтэй болгосноор хурдасгах боломжтой. Программист тохирох тэнцвэрийг олохыг шаарддаг.

Эхний экспонентацийн хувьд дэгжин бус гогцоо нь маш энгийн бөгөөд ойлгомжтой байдаг. Үүнийг рекурсоор солих нь утгагүй юм. Гэхдээ ихэнхдээ програмууд нь ийм нарийн төвөгтэй ойлголтуудтай тулгардаг тул унших чадварыг нэмэгдүүлэх замаар үр ашгийг бууруулахыг хүсдэг.

Нэгээс олон удаа давтагдсан, миний бүрэн зөвшөөрч байгаа үндсэн дүрэм бол програм удааширч байгаа гэдэгт бүрэн итгэлтэй байх хүртэл гүйцэтгэлийн талаар санаа зовох хэрэггүй юм. Хэрэв тийм бол хамгийн удаан эдэлгээтэй эд ангиудыг олж, үр ашгийг нь дээшлүүлэхийн тулд гоёмсог байдлыг арилга.

Мэдээжийн хэрэг, бид гүйцэтгэлийг шууд үл тоомсорлож болохгүй. Ихэнх тохиолдолд экспоненциалын нэгэн адил бид гоёмсог шийдлүүдээс тийм ч хялбар байдлыг олж авдаггүй. Заримдаа туршлагатай програмистэнгийн арга барил хэзээ ч хангалттай хурдан биш гэдгийг тэр даруй олж хардаг.

Хэт олон шинэхэн программистууд жижиг зүйлд ч гэсэн үр ашигтай байх хүсэл эрмэлзэлтэй байдаг тул би үүнийг онцолж байна. Үр дүн нь илүү том, илүү төвөгтэй бөгөөд ихэнхдээ алдаагүй байдаг. Ийм программыг бичихэд удаан хугацаа шаардагддаг ч ихэнхдээ тийм ч хурдан ажилладаггүй.

Гэхдээ рекурс нь үргэлж давталтын үр ашиг багатай хувилбар биш юм. Зарим асуудлыг рекурсийн тусламжтайгаар шийдвэрлэхэд хялбар байдаг. Ихэнхдээ энэ нь модны хэд хэдэн мөчрүүдийг дайрах явдал бөгөөд тус бүр нь салбарлаж болно.

Энд нэг оньсого байна: 1-ээс эхэлж, дараа нь 5-ыг нэмэх эсвэл 3-аар үржүүлснээр хязгааргүй тооны тоог гаргаж болно. Тоо өгөөд нэмэх, үржүүлэх дарааллыг олох гэж оролддог функцийг хэрхэн бичих вэ Энэ нь өгөгдсөн тоонд хүргэдэг үү? Жишээлбэл, 13-ын тоог эхлээд 1-ийг 3-аар үржүүлж, дараа нь 5-ыг хоёр удаа нэмснээр олж болно. Мөн 15-ын тоог ийм байдлаар олж авах боломжгүй юм.

Рекурсив шийдэл:

findSolution(target) ( функц олох(эхлэх, түүх) ( хэрэв (эхлэх == зорилтот) түүхийг буцаана; өөрөөр бол (эхлэх > зорилт) null буцаана; өөрөөр буцаана олох(эхлэх + 5, "(" + түүх + " + 5)") || олох(эхлэх * 3, "(" + түүх + " * 3)"); ) олох(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

Энэ жишээ нь хамгийн богино шийдлийг олох албагүй - энэ нь хэнд ч сэтгэл хангалуун байдаг. Таныг програм хэрхэн ажилладагийг шууд ойлгоно гэж би бодохгүй байна. Гэхдээ рекурсив сэтгэлгээний энэхүү гайхалтай дасгалыг ойлгоцгооё.

Find дотоод функц нь рекурс хийдэг. Үүнд хоёр аргумент шаардлагатай - одоогийн дугаар ба энэ тоонд хэрхэн хүрсэн тухай тэмдэглэл агуулсан мөр. Мөн энэ нь бидний алхмуудын дарааллыг харуулсан мөрийг буцаана, эсвэл null.

Үүнийг хийхийн тулд функц нь гурван үйлдлийн аль нэгийг гүйцэтгэдэг. Хэрэв өгөгдсөн тоо нь зорилттой тэнцүү бол одоогийн түүх нь яг түүнд хүрэх арга зам учраас буцаж ирдэг. Хэрэв өгөгдсөн тоо нь зорилгоос их байвал үржүүлж, нэмсээр байх нь утгагүй, учир нь энэ нь зөвхөн нэмэгдэх болно. Хэрэв бид зорилгодоо хүрч амжаагүй бол функц хоёуланг нь оролдоно боломжит арга замууд, өгөгдсөн тооноос эхэлнэ. Тэр өөрийгөө хоёр удаа, арга тус бүрээр нэг удаа дууддаг. Хэрэв эхний дуудлага null гэж буцахгүй бол буцаана. Өөр тохиолдолд хоёр дахь нь буцаж ирдэг.

Функц нь хүссэн үр дүндээ хэрхэн хүрч байгааг илүү сайн ойлгохын тулд 13 дугаарын шийдлийг олохын тулд түүний хийсэн дуудлагуудыг харцгаая.

Хай(1, "1") олох(6, "(1 + 5)") олох(11, "((1 + 5) + 5)") олох(16, "(((1 + 5) + 5 ) + 5)") хэт том олдвор(33, "(((1 + 5) + 5) * 3)") хэт том олдвор(18, "((1 + 5) * 3)") хэт том олдлоо( 3, "(1 * 3)") олох(8, "((1 * 3) + 5)") олох(13, "(((1 * 3) + 5) + 5)") олдсон!

Догол нь дуудлагын стекийн гүнийг харуулдаг. Эхний удаад олох функц нь (1 + 5) ба (1 * 3) -аас эхэлсэн шийдлүүдийг шалгахын тулд өөрийгөө хоёр удаа дууддаг. Эхний дуудлага нь (1 + 5) -аар эхэлсэн шийдлийг хайж олох бөгөөд шаардлагатай тооноос бага эсвэл тэнцүү тоо гаргаж буй бүх шийдлүүдийг шалгахын тулд рекурсийг ашигладаг. Үүнийг олохгүй, null буцаана. Дараа нь оператор || (1 * 3) сонголтыг шалгадаг функцийн дуудлага руу шилжинэ. Бид энд азтай байна, учир нь гурав дахь рекурсив дуудлагад бид 13-ыг авдаг. Энэ дуудлага нь мөрийг буцаадаг ба || замдаа энэ шугамыг илүү өндөрт дайран өнгөрч, шийдэл буцаана.

Өсөн нэмэгдэж буй функцууд Програмд ​​функцийг нэвтрүүлэх байгалийн хоёр арга байдаг.

Эхнийх нь та ижил төстэй кодыг хэд хэдэн удаа бичих явдал юм. Үүнээс зайлсхийх хэрэгтэй - илүү их код нь програмыг ойлгохыг оролдож буй хүмүүст илүү их алдаа гаргах зай, илүү их унших материал гэсэн үг юм. Тиймээс бид давтагдах функцийг авч, түүнд тааруулна сайн нэрмөн функцэд оруулна.

Хоёрдахь арга бол та тусдаа функцэд байрлуулахад тохиромжтой зарим шинэ функцийн хэрэгцээг олж мэдэх явдал юм. Та функцийн нэрээр эхэлж, дараа нь түүний биеийг бичнэ. Та функц өөрөө тодорхойлогдохоос өмнө функцийг ашигладаг кодыг бичиж эхэлж болно.

Функцийг нэрлэх нь танд хичнээн хэцүү байгаа нь түүний үйл ажиллагааг хэр сайн ойлгож байгааг харуулж байна. Нэг жишээ татъя. Бид ферм дэх үхэр, тахиа гэсэн хоёр тоог хэвлээд дараа нь “үнээ”, “тахиа” гэсэн үгийг хэвлэдэг программ бичих хэрэгтэй. Та урд байгаа тоонуудад тэг нэмэх хэрэгтэй бөгөөд тус бүр нь яг гурван байрлалыг эзэлнэ.

007 Үхэр 011 Тахиа

Бидэнд хоёр аргументтай функц хэрэгтэй нь ойлгомжтой. Кодлоод эхэлцгээе.
// хэвлэх FarmInventory функц printFarmInventory(үнээ, тахиа) ( var cowString = String(cows); while (cowString.length)< 3) cowString = "0" + cowString; console.log(cowString + " Коров"); var chickenString = String(chickens); while (chickenString.length < 3) chickenString = "0" + chickenString; console.log(chickenString + " Куриц"); } printFarmInventory(7, 11);

Хэрэв бид мөрөнд .length-ийг нэмбэл түүний уртыг авна. Энэ нь харагдаж байна while гогцоо 3 тэмдэгттэй тэмдэгт мөр авах хүртлээ тоонуудын толгойн тэгийг нэмнэ.

Бэлэн! Гэтэл бид яг тариачин руу код илгээх гэж байтал (мэдээж их хэмжээний чекийн хамт) тэр залгаж, фермдээ гахай байгаа, бид гахайн тоог харуулсан дэлгэц нэмж болох уу гэж хэлэв. хөтөлбөр?

Мэдээжийн хэрэг боломжтой. Харин эдгээр дөрвөн мөрний кодыг хуулж, буулгаж эхлэхэд бид зогсч, бодох хэрэгтэй гэдгийг ойлгодог. Илүү сайн арга зам байх ёстой. Бид хөтөлбөрийг сайжруулахыг хичээж байна:

// гаралт ОЛОН ТЭГ, шошго функц printZeroPaddedWithLabel(тоо, шошго) ( var numberString = String(тоо); while (numberString.length)< 3) numberString = "0" + numberString; console.log(numberString + " " + label); } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Коров"); printZeroPaddedWithLabel(chickens, "Куриц"); printZeroPaddedWithLabel(pigs, "Свиней"); } printFarmInventory(7, 11, 3);

Ажиллаж байна! Гэхдээ printZeroPaddedWithLabel гэдэг нэр нь жаахан хачирхалтай. Энэ нь гаралт, тэг нэмэх, шошго гэсэн гурван зүйлийг нэг функц болгон нэгтгэдэг. Функцэд бүхэл бүтэн давтагдах фрагмент оруулахын оронд нэг ойлголтыг онцолж үзье:

// Тэг функц нэмэх zeroPad(тоо, өргөн) ( var string = String(тоо); while (string.length)< width) string = "0" + string; return string; } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { console.log(zeroPad(cows, 3) + " Коров"); console.log(zeroPad(chickens, 3) + " Куриц"); console.log(zeroPad(pigs, 3) + " Свиней"); } printFarmInventory(7, 16, 3);

zeroPad сайхан, тодорхой нэртэй функц нь кодыг ойлгоход хялбар болгодог. Мөн энэ нь зөвхөн манай тохиолдолд төдийгүй олон нөхцөл байдалд ашиглагдаж болно. Жишээлбэл, форматлагдсан хүснэгтүүдийг тоогоор харуулах.

Онцлогууд хэр ухаалаг, олон талт байх ёстой вэ? Бид гурван байрлалд тэгтэй тоог хавсаргах энгийн функц эсвэл боловсронгуй функц бичиж болно. Ерөнхий зорилготоо форматлах, дэмжих бутархай, сөрөг тоо, цэгийн зэрэгцүүлэх, өөр тэмдэгтээр дүүргэх гэх мэт.

Сайн дүрэм бол зөвхөн ашигтай гэдгийг мэдэж байгаа функцийг нэмэх явдал юм. Заримдаа жижиг хэрэгцээнд зориулж ерөнхий зориулалтын хүрээг бий болгох нь сонирхол татдаг. Түүнийг эсэргүүц. Та ажлаа хэзээ ч дуусгахгүй, хэн ч ашиглахгүй олон тооны код бичээд л дуусах болно.

Функц ба гаж нөлөө Функцийг гаж нөлөөгөөр нь дууддаг болон ямар нэг үнэ цэнийг олж авахын тулд дуудагддаг гэж ойролцоогоор хувааж болно. Мэдээжийн хэрэг, эдгээр шинж чанаруудыг нэг функцэд нэгтгэх боломжтой.

Фермийн жишээн дэх анхны туслах функц болох printZeroPaddedWithLabel нь гаж нөлөөтэй тул дуудагдсан: энэ нь мөр хэвлэдэг. Хоёр дахь, zeroPad, учир нь буцах утга. Хоёрдахь функц нь эхнийхээс илүү олон удаа хэрэг болдог нь тохиолдлын хэрэг биш юм. Утга буцаах функцууд нь гаж нөлөө үүсгэдэг функцээс илүү бие биетэйгээ хослуулах нь илүү хялбар байдаг.

Цэвэр функц нь зөвхөн ямар ч гаж нөлөө үзүүлэхгүй, харин бусад кодын гаж нөлөөнөөс хамаардаггүй, жишээлбэл, санамсаргүй байдлаар тохиолдож болох глобал хувьсагчтай ажиллахгүй тусгай төрлийн үнэ цэнийг буцаах функц юм. өөр газар өөрчлөгдсөн. Цэвэр функц нь ижил аргументуудаар дуудагдах үед ижил үр дүнг буцаана (мөн өөр юу ч хийхгүй) - энэ нь үнэхээр сайхан юм. Түүнтэй ажиллахад амархан. Ийм функцэд дуудлага нь кодын утгыг өөрчлөхгүйгээр түүний ажлын үр дүнд оюун санааны хувьд орлуулж болно. Хэрэв та ийм функцийг туршихыг хүсвэл зүгээр л дуудаж болно, хэрэв энэ нь тухайн контекстэд ажиллаж байвал ямар ч нөхцөлд ажиллах болно гэдэгт итгэлтэй байх хэрэгтэй. Бага зэргийн цэвэр функцууд нь олон хүчин зүйлээс хамааран өөр өөр үр дүнг өгч болох ба туршилт, тооцоолоход хэцүү гаж нөлөө үзүүлдэг.

Гэсэн хэдий ч, та бүхэлдээ цэвэр биш функцуудыг бичих, эсвэл эдгээр функцүүдийн ариун кодыг цэвэрлэх ажлыг эхлүүлэхээс ичиж болохгүй. Гаж нөлөө нь ихэвчлэн ашигтай байдаг. console.log функцийн цэвэр хувилбарыг бичих арга байхгүй бөгөөд энэ функц нь маш хэрэгтэй. Зарим үйлдлүүд нь гаж нөлөөг ашиглан илэрхийлэхэд хялбар байдаг.

Дүгнэлт Энэ бүлэгт өөрийн функцуудыг хэрхэн бичихийг харуулсан. Функцийн түлхүүр үгийг илэрхийлэл болгон ашиглах үед функцийн дуудлага руу заагч буцаана. Заавар болгон ашиглах үед та хувьсагч руу функц дуудлагыг оноож зарлаж болно.

Функцуудыг ойлгох түлхүүр бол орон нутгийн хамрах хүрээ юм. Функцийн дотор зарласан параметр болон хувьсагч нь түүний локал шинж чанартай бөгөөд дуудах болгонд дахин үүсгэгддэг ба гаднаас нь харагдахгүй. Өөр функц дотор зарлагдсан функцууд нь түүний хамрах хүрээ рүү хандах боломжтой.

Програмын гүйцэтгэсэн янз бүрийн даалгавруудыг функцүүдэд хуваах нь маш ашигтай байдаг. Та өөрийгөө давтах шаардлагагүй; номын бүлэг, хэсгүүд нь ердийн текстийг зохион байгуулахад тусалдаг шиг функцууд нь кодыг утга учиртай хэсгүүдэд хуваах замаар кодыг илүү уншихад хялбар болгодог.

ExercisesMinimum Өмнөх бүлэгт бид хамгийн бага аргументыг буцаадаг Math.min функцийг дурдсан. Одоо бид ийм функцийг өөрсдөө бичиж болно. Хоёр аргумент авч хамгийн бага утгыг нь буцаадаг min функц бич.

Console.log(мин(0, 10)); // → 0 console.log(min(0, -10)); // → -10

Рекурс (Recursion) Бид % (modulo) операторыг ашиглан тоо (% 2) тэгш эсэхийг тодорхойлох боломжтойг харлаа. Үүнийг тодорхойлох өөр нэг арга байна:

Тэг тэгш байна.
Нэгж нь хачирхалтай.
Аливаа N тоо нь N-2-той ижил паритеттай байна.

Эдгээр дүрмийн дагуу isEven рекурсив функцийг бичнэ үү. Энэ нь тоо хүлээн авч, логик утгыг буцаах ёстой.

Үүнийг 50 ба 75-аар шалгаарай. -1 гэж өгөөд үзээрэй. Тэр яагаад ийм зан гаргаж байгаа юм бэ? Үүнийг ямар нэгэн байдлаар засах боломжтой юу?

Үүнийг 50 ба 75 дээр туршиж үзээрэй. -1 дээр хэрхэн ажиллаж байгааг хараарай. Яагаад? Та үүнийг засах арга бодож чадах уу?

Console.log(isEven(50)); // → үнэн console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??

Буурцаг тоолж байна.

Мөрний N тэмдэгтийн дугаарыг .charAt(N) ("string".charAt(5)) нэмснээр авч болно - .length ашиглан мөрийн уртыг авахтай адил. Буцах утга нь нэг тэмдэгтээс бүрдэх мөр байх болно (жишээлбэл, "k"). Мөрний эхний тэмдэгт нь 0 байрлалтай бөгөөд энэ нь сүүлчийн тэмдэгт нь string.length - 1 гэсэн байрлалтай байна. Өөрөөр хэлбэл, хоёр тэмдэгтийн тэмдэгт нь 2 урттай, тэмдэгтүүдийн байрлал нь 0 ба 1 байна.

Мөрийг аргумент болгон авч, мөрөнд байгаа "B" тэмдэгтүүдийн тоог буцаадаг countBs функц бич.

Дараа нь countChar хэмээх функцийг бичнэ үү, энэ нь countBs шиг ажилладаг боловч хоёр дахь параметрийг авдаг - бидний мөрөнд хайх тэмдэгт (зүгээр л "B" тэмдэгтүүдийн тоог тоолохын оронд). Үүнийг хийхийн тулд countBs функцийг дахин ажиллуулна уу.

Функцууд нь JavaScript дахь кодын хамгийн чухал барилгын блокуудын нэг юм.

Функцууд нь олон тооны командуудаас бүрдэх ба ихэвчлэн нэгийг гүйцэтгэдэг тодорхой даалгавар(жишээлбэл, тоог нэгтгэх, үндсийг тооцоолох гэх мэт).

Функцид байрлуулсан код нь зөвхөн энэ функцийг тодорхой дуудсаны дараа л гүйцэтгэгдэх болно.

Функцийн тунхаглал

1. Синтакс:

//Functionname(ln1, ln2)( Функцийн код) //Functionname(ln1,lr2) функцийг дуудаж байна;

2. Синтакс:

//Функцийн мэдэгдэл var function name=function(ln1, ln2)(Функцийн код) //Функцийн функцийг дуудаж байна(ln1,lr2);

functionname нь функцийн нэрийг зааж өгдөг. Хуудасны функц бүр өвөрмөц нэртэй байх ёстой. Функцийн нэрийг латин үсгээр зааж өгөх ёстой бөгөөд тоогоор эхэлж болохгүй.

ln1 ба ln2 нь функцэд дамжуулж болох хувьсагч эсвэл утгууд юм. Функц бүрт хязгааргүй тооны хувьсагч дамжуулж болно.

Анхаарна уу: функцэд хувьсагч дамжуулагдаагүй байсан ч функцын нэрний ард "()" хаалт оруулахаа бүү мартаарай.

JavaScript дахь функцын нэрс нь жижиг жижиг үсгийг харгалздаг гэдгийг анхаарна уу.

JavaScript функцийн жишээ

Доорх жишээн дээрх messageWrite() функц зөвхөн товчлуурыг дарсны дараа л ажиллана.

Энэ жишээнд onclick үйл явдлыг ашигладаг болохыг анхаарна уу. JavaScript үйл явдлуудЭнэ сурах бичгийн дараа дэлгэрэнгүй авч үзэх болно.

// Функц нь хуудасны функцэд text бичдэг messageWrite() ( document.write("Энэ текстийг JavaScript ашиглан хуудсанд бичсэн!"); )

Хувьсагчдыг функцэд шилжүүлэх

Та функцэд хязгааргүй тооны хувьсагч дамжуулж болно.

Анхаарна уу: функцүүдийн доторх хувьсагчтай бүх залруулга нь үнэндээ хувьсагчид өөрсдөө биш, харин тэдгээрийн хуулбар дээр хийгддэг тул функцийг гүйцэтгэсний үр дүнд хувьсагчдын агуулга өөрөө өөрчлөгддөггүй.

/* Дамжуулсан хувьсагчид 10-ыг нэмээд үр дүнг нь хуудсанд харуулах функцийг тодорхойлъё */ function plus(a)( a=a+10; document.write("Функцийн гаралт: " + a+"
"); ) var a=25; document.write("Функц дуудлагын өмнөх хувьсагчийн утга: "+a+"
"); // a plus(a) хувьсагчийг дамжуулж функцийг дууд; document.write("Функцийг дуудсаны дараах хувьсагчийн утга: "+a+"
");

Түргэн харах

Глобал хувьсагч руу түүний хуулбараас биш функцээс хандахын тулд window.variable_name ашиглана уу.

Function plus(a)( window.a=a+10; ) var a=25; document.write("Функц дуудлагын өмнөх хувьсагчийн утга: "+a+"
"); plus(a); document.write("Функцийг дуудсаны дараах хувьсагчийн утга: "+a+"
");

Түргэн харах

буцах тушаал

Буцах командын тусламжтайгаар та функцээс утгыг буцаах боломжтой.

//Нийлбэр функц нь өөрт нь шилжүүлсэн хувьсагчдын нийлбэрийг буцаана sum(v1,v2)( буцаах v1+v2; ) document.write("5+6=" + sum(5,6) + "
"); document.write("10+4=" + нийлбэр(10,4) + "
");

Түргэн харах

Суулгасан функцууд

Хэрэглэгчийн тодорхойлсон функцүүдээс гадна JavaScript нь суулгасан функцуудтай.

Жишээлбэл, суулгасан isFinite функц нь дамжуулсан утга нь зөв тоо эсэхийг шалгах боломжийг танд олгоно.

Document.write(isFinite(40)+"
"); document.write(isFinite(-590)+"
"); document.write(isFinite(90.33)+"
"); document.write(isFinite(NaN)+"
"); document.write(isFinite("Энэ бол мөр")+"
");

Түргэн харах

Жич: бүрэн жагсаалтсуурилуулсан JavaScript функцуудТа үүнийг манайхаас олж болно.

Орон нутгийн болон глобал хувьсагчид

Функц дотор үүсгэсэн хувьсагчдыг локал хувьсагч гэж нэрлэдэг. Та ийм хувьсагчдад зөвхөн тодорхойлсон функцүүдийн хүрээнд хандах боломжтой.

Функцийн кодыг гүйцэтгэсний дараа ийм хувьсагч устана. Энэ нь ижил нэртэй хувьсагчдыг өөр өөр функцээр тодорхойлж болно гэсэн үг юм.

Функцийн кодын гадна үүсгэгдсэн хувьсагчдыг глобал хувьсагч гэж нэрлэдэг бөгөөд ийм хувьсагчдад кодын хаанаас ч хандах боломжтой.

Хэрэв та функц дотор var-гүй хувьсагчийг зарлавал энэ нь мөн глобал болно.

Глобал хувьсагчдыг хуудас хаагдсаны дараа л устгадаг.

//var1 болон var2 глобал хувьсагчдыг зарлах var var1="var1 байгаа"; var var2; function func1() ( //func1 var var2="var2" функц дотор var2 утгыг оноох; ) //Өөр функцээс var1 болон var2 хувьсагчийн агуулгыг func2() хуудасны функцэд хэвлэх ( //Хэвлэх var1 document.write хувьсагчийн агуулга ( var1 + "
"); //var2 document.write(var2); ) хувьсагчийн агуулгыг гарга.

Түргэн харах

Дэлгэц дээр хэвлэх үед var2 нь хоосон утгатай байх болно, учир нь func1 var2-ын локал "хувилбар" дээр ажилладаг.

Нэргүй функцуудыг ашиглах

Зарлагдсан үед нэр агуулаагүй функцуудыг нэргүй гэж нэрлэдэг.

Нэргүй функцүүд нь ердийн функцүүд шиг кодоос дараа нь дуудагдахгүй, харин бусад функцууд руу параметр болгон дамжуулагдахаар зарлагдсан байдаг.

arrMap(arr,func)( var res=new Array; for (var i=0;i) функц