If you're seeing this message, it means we're having trouble loading external resources on our website.

Ако си зад уеб филтър, моля, увери се, че домейните *. kastatic.org и *. kasandbox.org са разрешени.

Основно съдържание

Обект от тип Button

Един от най-добрите начини да направиш кода си преизползваем и мощен е да използваш обектно-ориентирано програмиране, особено когато използваш UI контроли като бутони. В обектно-ориентираното програмиране представяме нашия програмен свят с помощта на абстрактни обектни типове, които имат определено поведение, а след това създаваме от тях конкретни инстанции с конкретни параметри. Ако не си спомняш как се прави това в JavaScript, можеш да си пропомниш тук.
За да използваме ООП, за да правим бутони, ще трябва да дефинираме типа Button, а след това и неговите методи, например да го нарисуваме и да прихванем кликване с мишката. Бихме искали да можем да напишем код, който да изглежда така:
var btn1 = new Button(...);
btn1.draw();

mouseClicked = function() {
  if (btn1.isMouseInside()) {
     println("Ей, кликна върху мен!");
  }
}
Да сравним това с кода, който написахме в последната статия:
var btn1 = {...};
drawButton(btn1);

mouseClicked = function() {
  if (isMouseInside(btn1)) {
     println("Ей, кликна върху мен!");
  }
}
Много си приличат, нали? Но между тях има голяма разлика – всички функции са дефинирани в типа Button, те принадлежат на бутоните. Има по-силно свързване на свойства и поведение, а това води до по-чист и по-преизползваем код.
За да дефинираме типа Button, започваме с конструктор: специалната функция, която конфигурира параметрите и задава началните свойства на инстанцията на обекта.
За начало ето един конструктор, който приема x, y, width и height:
var Button = function(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
};

var btn1 = new Button(100, 100, 150, 150);
Това със сигурност работи, но аз препоръчвам друг подход. Вместо да приема отделни параметри, конструкторът може да приеме обект.
var Button = function(config) {
    this.x = config.x;
    this.y = config.y;
    this.width = config.width;
    this.height = config.height;
    this.label = config.label;
};
Предимството на приемането на обект е, че можем да добавяме допълнителни параметри, които конструкторът да обработва (като label), и за нас ще е лесно да разбираме какво прави всеки параметър, когато създаваме бутон:
var btn1 = new Button({
    x: 100, y: 100,
    width: 150, height: 50,
    label: "Кликни, моля!"});
Но можем да отидем и още една стъпка напред. Ами ако повечето ни бутони са с еднаква ширина и височина? Тогава не е необходимо всеки път да задаваме параметрите за височина и ширина за всеки бутон, a можем да ги задаваме само когато е необходимо. Можем да кажем на нашия конструктор да проверява дали свойството е дефинирано в конфигурационния обект и, ако не е, да му задава стойност по подразбиране. Ето така:
var Button = function(config) {
    this.x = config.x || 0;
    this.y = config.y || 0;
    this.width = config.width || 150;
    this.height = config.height || 50;
    this.label = config.label || "Клик";
};
Сега можем просто го зададем с подмножество от свойства, защото на другите ще бъде присвоена стойност по подразбиране:
var btn1 = new Button({x: 100, y: 100, label: "Кликни, моля!"});
Доста работа за един конструктор, нали? Но, кълна се, заслужава си.
След като направихме нашия конструктор, нека да дефинираме част от поведението: метода draw. Той ще бъде същият като функцията drawButton, но ще взима свойства от this, тъй като е дефиниран в самия прототип на обекта:
Button.prototype.draw = function() {
    fill(0, 234, 255);
    rect(this.x, this.y, this.width, this.height, 5);
    fill(0, 0, 0);
    textSize(19);
    textAlign(LEFT, TOP);
    text(this.label, this.x+10, this.y+this.height/4);
};
След като го дефинираме, можем да го извикаме така:
btn1.draw();
Ето една програма, която използва обекта Button, за да създаде 2 бутона – забележете колко лесно е да създадете и нарисувате няколко бутона:
Ние обаче пропуснахме трудната част: прихващането на кликове. Можем да започнем като дефинираме функция в прототипа Button, която да връща true, ако потребителят кликне в границите на конкретен бутон. Отново, тази функция е същата като старата, но взима всичките си свойства от this, вместо да бъдат подавани на обекта:
Button.prototype.isMouseInside = function() {
    return mouseX > this.x &&
           mouseX < (this.x + this.width) &&
           mouseY > this.y &&
           mouseY < (this.y + this.height);
};
Сега можем да я използваме във функцията mouseClicked:
mouseClicked = function() {
    if (btn1.isMouseInside()) {
        println("Направи правилен избор!");
    } else if (btn2.isMouseInside()) {
        println("Ура, избра мен!");
    }
};
Пробвайте по-долу, щракнете върху всеки от бутоните:
Но има нещо, което ме дразни в начина, по който прихванахме този клик. Целият смисъл на обектно-ориентираното програмиране е да събере цялото поведение, свързано с обекта вътре в самия обект и да използва свойства, за да има различно поведение. Но ние оставихме част от поведението извън обекта, printlnвсе още е в mouseClicked:
mouseClicked = function() {
    if (btn1.isMouseInside()) {
        println("Направи правилен избор!");
    } else if (btn2.isMouseInside()) {
        println("Ура, избра мен!");
    }
};
Тези команди за принтиране трябва да са по-здраво свързани с всеки бутон по някакъв начин, като нещо, което подаваме на конструктора. Ако погледнем начина, по който са написани сега, можем да решим да подадем съобщение на конструктора и да дефинираме функция handleMouseClick, която да го отпечата:
var Button = function(config) {
    ...
    this.message = config.message || "Кликнато!";
};

Button.prototype.handleMouseClick = function() {
    if (this.isMouseInside()) {
         println(this.message);
    }
};

var btn1 = new Button({
    x: 100,
    y: 100,
    label: "Кликни, моля!",
    message: "Направи правилен избор!"
});

mouseClicked = function() {
   btn1.handleMouseClick();
};
Така е много по-добре, тъй като сега всичко, свързано с поведението на всеки бутон, е запазено в конструктора. Но е и доста просто. Ами ако искаме да направим още нещо, освен да печатаме съобщение, например да нарисуваме форма или да променим сцената – нещо, което ще отнеме няколко реда код? В този случай бихме искали да осигурим на конструктора повече от един символен низ – ще искаме да му осигурим някакъв код. Как можем да подаваме код?
...С функция! В JavaScript (но не във всички езици), можем да подадем функции като параметри на функциите. Това е полезно в много ситуации, но е особено полезно, когато дефинираме поведението на UI контроли като бутоните. Можем да кажем на бутона, "Хей, това е един бутон, ето малко код, който искам да извикаш, когато потребителят кликне върху бутона." Наричаме тези функции "callback" (колбек) функции, защото те не се извикват непосредствено, а се извикват като отговор в подходящо време.
Можем да започнем, като подадем параметъра onClick, който е функция:
var btn1 = new Button({
    x: 100,
    y: 100,
    label: "Кликни, моля!",
    onClick: function() {
       text("Направи правилен избор!", 100, 300);
    }
});
След това можем да се уверим, че конструкторът ни задава свойството onClick според това, което сме подали. По подразбиране, в случай, че не сме подали onClick, просто ще създадем функция, която не изпълнява операции. Тя е там, за да можем да я извикаме, без да получим грешка:
var Button = function(config) {
    // ...
    this.onClick = config.onClick || function() {};
};
Накрая трябва да извикаме колбек функцията, когато потребителят кликне върху бутона. Това е доста просто – можем да я извикаме, като напишем името на свойството, в което сме я запазили, и го последваме с празни скоби:
Button.prototype.handleMouseClick = function() {
    if (this.isMouseInside()) {
        this.onClick();
    }
};
И сме готови – имаме обект от типа Button, с който лесно можем да създаваме нови бутони, като правим така, че всеки бутон да изглежда различно и да отговаря по различен начин на кликване. Кликни по примера, който е даден, и ще видиш какво се случва, когато променяш параметрите на бутона:
Сега, щом вече имаш нещо като шаблон за бутоните, можеш да персонализираш бутоните си по друг начин, например да добавиш различни цветове или да ги накараш да отговарят на други събития. Опитай в своите програми!

Искаш ли да се присъединиш към разговора?

Все още няма публикации.
Разбираш ли английски? Натисни тук, за да видиш още дискусии в английския сайт на Кан Академия.