Цыклы

Масівы канешне клёва, але навошта яны, калі немагчыма працаваць з асобнымі элементамі. Таму і існуюць цыклы. Вось напрыклад:

  • var languages = ['HTML', 'CSS', 'JavaScript'];
  • languages.forEach(function(language) { alert('I love ' + language + '!'); });

Каб пісаць шматрадковы код у кансолі Вы можаце націскаць Shfit + Enter, каб пераскачыць на новы радок, а код не выканаць. Або можна карыстацца JSFiddle – сайт які дазваляе проста пісаць і зьмяняць JavaScript.

Давайце паглядзім як гэта працуе:

  1. Ствараецца масіў радкоў.
  2. Выклікаецца мэтад forEach ў масіва.
  3. Функцыя зваротнага выкліку (callback) перадаецца ў forEach, з параметрам які завецца language.
  4. Выбіраецца першы элемент масіва 'HTML' і прысвойваецца ў зьменную language.
  5. Зьяўляецца алерт, які кажа што Вы любіце HTML.
  6. Паўтараюцца крокі 4 і 5 для астатніх элементаў у масіве languages.

Мы можам выкарыстоўваць цыклы для больш цікавых задач. Мы пісалі код, які падлічвае суму двух лікаў, а што калі іх больш?

  • var total = 0;
  • summands = [1, 2, 3, 4, 5];
  • summands.forEach(function(summand) { total += summand; });
  • total

Назва масіва звычайна пішацца ў множным ліку, а параметар, які перадаецца ў forEach мае адзіночны (напрыклад kittens для масіва kitten для аргумэнта функцыі).

Зараз давайце створым невялічкую праграму-гульню “Звяр’яцелая Бібліятэка”, якая будзе пытаць у чалавека некалькі слоў і потым распавядаць гісторыю выкарыстоўваючы іх.

mad-lib.html
<div id="questions">
    <h1>Please fill out the following!</h1>
    <hr>
    <form id="story-form" role="form">
        <div class="form-group">
            <label for="person">Boy's Name</label>
            <input id="person" class="form-control person" type="text" placeholder="Enter a boy's name ..">
        </div>
        <div class="form-group">
            <label for="adjective">Adjective</label>
            <input id="adjective" class="form-control adjective" type="text" placeholder="Enter an adjective ..">
        </div>
        <div class="form-group">
            <label for="noun">Plural noun</label>
            <input id="noun" class="form-control noun" type="text" placeholder="Enter a plural noun ..">
        </div>
        <div class="form-group">
            <label for="insect">An insect, plural</label>
            <input id="insect" class="form-control insect" type="text" placeholder="Enter an insect, plural ..">
        </div>
        <div class="form-group">
            <label for="noun2">Plural noun</label>
            <input id="noun2" class="form-control plural-noun" type="text" placeholder="Enter a plural noun ..">
        </div>
        <div class="form-group">
            <label for="verb">A verb ending in "s"</label>
            <input id="verb" class="form-control verb" type="text" placeholder="Enter a verb ending in 's ..">
        </div>
        <div>
            <button id="btn-click" type="submit" class="btn btn-primary btn-lg">Generate!</button>
        </div>
    </form>
    <hr>
</div>

<div id="story">
    <h1>Mad with the Libs</h1>
    <hr>
    <p>I'm in love with <span class="person"></span>. He's
        so <span class="adjective"></span>! He has big flat <span class="noun"></span>,
        and when our <span class="insect"></span> meet, I get <span class="noun2"></span>
        in my stomach. I've fallen for him like a ton of <span class="verb"></span>, and he shuffles for
        me, too. But I think he's got another girlfriend. What should I do?</p>
    <div>
        <button type="submit" class="btn btn-primary btn-lg" id="play-btn">Play Again?</button>
    </div>
</div>

І Javascript, які апрацоўвае вынікі:

scripts.js
$(document).ready(function() {

  $("#story").hide();

  $("#story-form").submit(function(e) {

    $(".person").empty().append($("input.person").val());
    $(".adjective").empty().append($("input.adjective").val());
    $(".noun").empty().append($("input.noun").val());
    $(".insect").empty().append($("input.insect").val());
    $(".noun2").empty().append($("input.plural-noun").val());
    $(".verb").empty().append($("input.verb").val());

    // show the story
    $("#story").show();

    // empty the form's values
    $(':input').val('');

    // hide the questions
    $("#questions").hide();

  });

  $("#play-btn").click(function(e) {
    $("#questions").show();
    $("#story").hide();
  });

});

Паглядзіце на гэты код. Ці не падаецца Вам што тут вельмі шмат дзеянняў які паўтараюцца? Ці можам мы што-небудзь спрасціць выкарыстоўваючы цыклы? Мне здаецца так:

scripts.js
$(document).ready(function() {
    $("#story-form").submit(function(event) {
        var blanks = ["person", "adjective", "noun", "insect", "noun2", "verb"];

        blanks.forEach(function(blank) {
            var userInput = $("input#" + blank).val();
            $("." + blank).text(userInput);
        });

        $("#story").show();

        event.preventDefault();
    });
});

Яшчэ адно вельмі добрае сцвярджэньне пра праграмнае забеспячэнне, гэта тое што яно пастаяна зьмяняецца. Заўсёды будзе новы функцыянал, які неабходна дадаць, заўсёды будуць багі, якія трэба пафіксіць. Часта людзі, які працуюць над ПЗ будуць зьмяняцца, і нават калі не, першапачатковым распрацоўшчыкам будзе патрэбна зьмяняць код, які быў напісаны месяцы ці нават гады таму, пра які яны нават не памятаюць.

There's a principle in software development called Don't Repeat Yourself, or DRY. DRY code is easy to change, because you only have to make any change in one place. One way we DRY up our code is by taking repetitive bits of code and extracting them into a function. Another way is by taking something we do several times and by turning it into a loop. Whenever you finish writing some code, you should always look back to see if there is any way you can DRY it up.

Вельмі клёвая рэч у новай версіі “Звар’яцелай Бібліятэкі” – гэта тое, што код не паўтараецца, што значыць яго лёгка зьмяняць. Раней мы рабілі адну і тую ж рэч 6 разоў: узяць значэньне інпута, памясціць у адпаведнае пустое месца. Калі мы бы пажадалі зьмяніць што-небудзь у кодзе, напрыклад пісаць кожнае ўведзенае слова з вялікай літары нам спатрэбілася зьмяняць яго ў шасці месцах. Змяніўшы код, мы пакінулі толькі адзін радок, дзе патрабаецца рабіць зьмену. Чым больш будуць рабіцца Вашыя праграмы тым больш Вы будзеце разумець, што паўтараючыся код стварае толькі праблемы – Вы можаце забыць абнавіць яго ва ўсіх месцах, ці, што горш, абнавіць па-рознаму.

Напачатку цыклы падаюцца трохі складанымі для разуменьня, таму прыктыкуйцеся пакуль ня будзеце адчуваць сябе на 100% упэўнена:

  • Перадайце прывітаньне кожнаму з Вашых сяброў выкарыстоўваючы alert() і масіў з іх імёнамі.
  • Напішыце праграму, якая будзе падлічваць здабытак лікаў.

Цыкл for

forEach() – гэта вельмі просты для разуменьня мэтад, але ён быў дададзены не так даўно ў JavaScript. Раней цыклы выглядалі вось так:

for (var index = 1; index <= 3; index += 1) {
    alert(index);
}

Всоь як працуе цыкл for:

  1. Апэратар for прымае тры аргумэнта: ініцыялізуючы параметр, умова і інкрэментуючы выраз.
  2. ініцыялізуючы параметр дазваляе праініцыялізаваць зьменную. У дадзеным выпадку мы праініцыялізавалі зьменную index лікам 1.
  3. Умова кажа цыклу for калі спыніць выконвацца. У дадзеным выпадку мы сказалі, каб цыкл спыніў выконвацца калі index дасягне 3.
  4. Інкрэментуючы выраз кажа дадаваць 1 да параметра index кожны раз пасьля прахода па цыклу.
  5. На кожным праходзе па цыклу мы проста адлюстроўваем алерт з бягучым значэньнем індэкса.

Вось яшчэ прыклад. Код, які мы бачылі раней, перапісаны выкарыстоўваючы цыкл for:

var languages = ['HTML', 'CSS', 'Javascript'];
for (var index = 0; index < languages.length; index += 1) {
    alert('I love ' + languages[index] + '!');
}

Вы напэзна заўважылі – гэты код мае нешта новае: languages.length вяртае даўжыню масіва, але гэта не мэтад – няма дужак. Гэта уласьцівасьць, пра якую мы даведаемся трохі пазьней. Радкі таксама маюць уласьцівасьці, напрыклад: 'foobar'.length.

Мэтад forEach() нашмат чысьцей за for, але часам Вы будзеце знаходзіцца ў сітуацыях, калі неабходна выканаць нейкі код пэўную колькасьці разоў.

Вашая чарга:

  • Ёсьць такая песня, якая завецца "99 bottles of beer on the wall". Напішыце праграму, якая будзе генераваць тэкс гэтай песьні.

Адладка праграмы

Чым болей робяцца Вашыя праграмы, тым складаней знайсьці і вызначыць прычыну чаму яны ламаюцца. Давайце даведаемся што такое адладка і як яна можа дапамагчы.

Як прыклад давайце разглядзім “Звар’яцелую Бібліятэку”, але дададзім у яе трохі памылак:

madlibs.html
<html>
<head>
    <link href="css/bootstrap.css" rel="stylesheet" type="text/css">
    <link href="css/styles.css" rel="stylesheet" type="text/css">
    <script src="js/jquery-2.1.1.js"></script>
    <script src="js/scipts.js"></script>
    <title>A fantastical adventure</title>
</head>
<body>
<div class="container">
    <div id="questions">
        <h1>Please fill out the following!</h1>
        <hr>
        <form id="story-form" role="form">
            <div class="form-group">
                <label for="person">Boy's Name</label>
                <input id="person" class="form-control" type="text" placeholder="Enter a boy's name ..">
            </div>
            <div class="form-group">
                <label for="adjective">Adjective</label>
                <input id="adjective" class="form-control" type="text" placeholder="Enter an adjective ..">
            </div>
            <div class="form-group">
                <label for="noun">Plural noun</label>
                <input id="noun" class="form-control" type="text" placeholder="Enter a plural noun ..">
            </div>
            <div class="form-group">
                <label for="insect">An insect, plural</label>
                <input id="insect" class="form-control" type="text" placeholder="Enter an insect, plural ..">
            </div>
            <div class="form-group">
                <label for="noun2">Plural noun</label>
                <input id="noun2" class="form-control" type="text" placeholder="Enter a plural noun ..">
            </div>
            <div class="form-group">
                <label for="verb">A verb ending in "s"</label>
                <input id="verb" class="form-control" type="text" placeholder="Enter a verb ending in 's">
            </div>
            <div>
                <button id="btn-click" type="submit" class="btn btn-primary btn-lg">Generate!</button>
            </div>
        </form>
        <hr>
    </div>
    <hr>
</div>

<div id="story">
    <h1>Mad with the Libs</h1>
    <hr>
    <p>I'm in love with <span class="person"></span>. He's
        so <span class="adjective"></span>! He has big flat <span class="noun"></span>,
        and when our <span class="insect"></span> meet, I get <span class="noun2"></span>
        in my stomach. I've fallen for him like a ton of <span class="verb"></span>, and he shuffles for
        me, too. But I think he's got another girlfriend. What should I do?</p>
    <div>
        <button type="submit" class="btn btn-primary btn-lg" id="play-btn">Play Again?</button>
    </div>
</div>
</body>
</html>
scripts.js
$(document).ready(function() {
    $("#story").hide();
    $("#story-form").submit(function() {
        var blanks = ["person", "adjective", "noun", "insect", "noun2", "verb"];

        blanks.forEach(function(blank) {
            var userInput = $("input." + blank).val();
            $("." + blank).text(userInput).val();
        });

        $("#story").sho();
    });
});

Калі я адчыняю старонку, запаўняю інфармацыю і ціскаю кнопку – нічога не адбываецца. Час адлажваць!

Першае што мы вавінны зрабіць – гэта знайсьці памылкі ў кансолі. Калі мы адчынім кансоль JavaScript там будзе палмыка: GET http://localhost:63342/lesson8/js/scipts.js index.html:7. Гэта памылка кажа нам што браўзер спрабаваў адчыніць файл scipts.js але не знайшоў яго. Вядома, гэта наша памылка – мы забыліся дадаць r у scripts.js. Вы проста не можаце сабе ўявіць наколькі гэта распаўсюджаная памылка. Давайце выправім памылку і перазагрузім старонку.

Ну вось, няма больш памылак у кансолі. Але калі мы зноў дасылаем нашую форму – мы нічога не атрымліваем. Давайце паспрубем яшчэ адну тактыку: паўза выкананьня коду калі адбываюцца памылкі. Калі мы адчынім нашую JavaScript кансоль і пераключым укладку на Sources, там зверху ёсьць кнопка – васьмікутнік з сымбалам паўзы. Калі мы ціснем па ёй яна робіцца фіялетавай. Гэта значыць што JavaScript спыніць выкананьне калі адбудзецца памылка. Зараз, калі мы ціскаем па кнопцы праграма спыняецца ў месцы дзе адбылася памылка. Назва мэтада напісана неправільна: $("#story").sho(); павінна быць $("#story").show();.

Давайце выправім памылка і будзе рухацца далей. Зараз мы ня маем ніводнай памылка, але форма ўсё яшчэ не працуе. Паспрабуем іншы падыход: правераць ці выконваецца код наогул. Я зраблю гэта дадаючы alert()‘ы у некалькі кропак у кодзе:

scripts.js
$(document).ready(function() {
    $("#story").hide();
    $("#story-form").submit(function() {
        alert('Got to beginning of form submit!');
        var blanks = ["person", "adjective", "noun", "insect", "noun2", "verb"];

        blanks.forEach(function(blank) {
            var userInput = $("input." + blank).val();
            $("." + blank).text(userInput).val();
        });

        $("#story").show();
        alert('Got to end of form submit!');
    });
});

Зараз можна ўпэўніцца ў тым, што увесь код выкодваецца. У дадзеным выпадку зьяўляюцца два дыялогавых акенца – таму ўсё ок. Каб зразумець у чым праблема, давайце вернемся трохі назад. Калі праглядзець занятак з формамі ў jQuery можна заўважыць што мы забыліся дадаць event.preventDefault() у функцыю-апрацоўшчык формы. Давайце дададзім яе:

scripts.js
$(document).ready(function() {
    $("#story").hide();
    $("#story-form").submit(function(event) {
        var blanks = ["person", "adjective", "noun", "insect", "noun2", "verb"];

        blanks.forEach(function(blank) {
            var userInput = $("input." + blank).val();
            $("." + blank).text(userInput).val();
        });

        $("#story").show();
        event.preventDefault()
    });
});

Зараз наш параграф з гісторыя ў нарэшце адлюстроўваецца!

Але мы ўсё яшчэ маем праблему – тое што мы ўводзім у форму не дадаецца ў параграф. Дзесьці існуе памылка, але вельмі складана зразумець дзе. Ці не было б выдатна каб мы маглі выконваць толькі адзін радок коду крок за крокам, каб бачыць, што ламаецца? Chrome мае зручную інструмэнт для такіх выпадкаў, які завецца адладчык (debugger). Скарыстацца ім можна дадаючы ключавое слова debugger у код. Як тут:

scripts.js
$(document).ready(function() {
    $("#story").hide();
    $("#story-form").submit(function(event) {
        var blanks = ["person", "adjective", "noun", "insect", "noun2", "verb"];

        blanks.forEach(function(blank) {
            debugger;
            var userInput = $("input." + blank).val();
            $("." + blank).text(userInput).val();
        });

        $("#story").show();
        event.preventDefault()
    });
});

Зараз, кожны раз калі JavaScript сустракаецца з радком debugger, ён будзе спыняць выкананьне і дазваляць нам выконваць любы код, які мы пажадаем. Так будзе адбывацца толькі тады, калі JavaScript-кансоль адчынена. Калі Вы паспрабуеце даслаць форму, JavaScript спыніцца ў месцы дзе Вы напісалі ключавое слова. Як толькі код спыніцца – адчыніце кансоль. Зараз мы можам выконваць код нібыта ён знаходзіцца прама ў тым месцы, дзе і debugger.

Паспрабуйце выканаць у кансолі: var userInput = $("input." + blank).val();. Вяртаецца undefined. Што не так? Гэты код пры першай ітэрацыі павінен вяртаць імя чалавека. Надрукуйце blank каб паглядзець што ўнутры. Вядома там person1. Тада давайце паспрабуем выканаць $("input." + blank); каб упэўніцца што мы выбіраем правільны элемент. Вось і праблема – нам вяртаецца пусты масіў. Гэта не тое, чаго мы чакалі. Калі паглядзім на наш HTML, мы пабачым што элементы маюць ID, а не класы, таму мы павінны выкарыстоўваць #s, замест . у селектарах. Паспрабуйце спачатку ў кансолі: $("input#" + blank); вяртае тое, што трэба. Калі мы вернемся ў наш код, выдалім debugger і абнавім селектары, наша старонка нарэшце пачне працаваць.

Апошня клёвая рэч у JavaScript якую можна выкарыстоўваць для адладкі гэта мэтад console.log(). Давайце вернемся ў наш код і замест debugger паспрабуем console.log():

scripts.js
$(document).ready(function() {
    $("#story").hide();
    $("#story-form").submit(function(event) {
        var blanks = ["person", "adjective", "noun", "insect", "noun2", "verb"];

        blanks.forEach(function(blank) {
            var userInput = $("input." + blank).val();
            console.log(userInput);
            $("." + blank).text(userInput).val();
        });

        $("#story").show();
        event.preventDefault()
    });
});

Зараз, калі Вы запоўніце і дашлеце форму ў кансолі будзе адлюстроўвацца значэньне, якое мае зьменная userInput на кожнай ітэрацыі. Часцей за ўсё для адладкі дастаткова выкарыстоўваць менавіта console.log(), а debugger пакінуць для зусім складаных выпадкаў.

Пінг-Понг

Напісаньне гульні Пінг-Понг дазваляе Вам праверыць свае веды па масівам, радкам, функцыям і разгалінаваньню. Гульня заключаецца ў тым, каб спытаць у карыстальніка лік а потым адлюстраваць усе лікі, якія знаходзяцца да ўведзенага ліку. Але, для тых лікаў, якія падзяляюца на тры друкаваць слова "ping" замест ліка, для тых, хто падзяляецца на пяць друкаваць "pong". Для тых хто падзяляецца на абодва тры і пяць друкаваць "ping-pong".

Падказка: Каб вызначыць ці падзяляецца лік на іншы лік выкарыстоўвайце аператар %. Напрыклад, калі зьменная завецца number і падзяляецца на 7, тады number % 7 верне 0.

Папярэдні занятак Наступны занятак