ผลต่างระหว่างรุ่นของ "01219245/javascript1/tutorial4"

จาก Theory Wiki
ไปยังการนำทาง ไปยังการค้นหา
 
(ไม่แสดง 44 รุ่นระหว่างกลางโดยผู้ใช้คนเดียวกัน)
แถว 57: แถว 57:
  
 
What do we see here?  Do you see any data?  Do you see any "behavior" or "operations with data"?  This is a nice indicator that we can group the data and functions related to this piece of data into an object.
 
What do we see here?  Do you see any data?  Do you see any "behavior" or "operations with data"?  This is a nice indicator that we can group the data and functions related to this piece of data into an object.
 +
 +
Let's call it, for now, a hangman word and think about the operations we would like to perform with it.
 +
 +
'''Hangman word''':
 +
* We want to check if the word contains an alphabet.
 +
* We want to get hints (e.g., something like "_ E _ _ O")
 +
 +
To do so, we should create the hangman word object with the "hidden word", i.e., hangman word should know the word to guess.
 +
 +
With this in mind, we can think about the object's ''interface'', i.e., how we should interact with the object.  Let's think about this in a code example.
 +
 +
<syntaxhighlight lang="javascript">
 +
  var hangman = new HangmanWord( 'ELEPHANT' );      // how a new object is created.
 +
 
 +
  // Sample interaction in the JavaScript console
 +
  hangman.getHint()                  // --> "_ _ _ _ _ _ _ _"
 +
  hangman.check( 'Z' )              // --> false
 +
  hangman.check( 'A' )              // --> true
 +
  hangman.getHint()                  // --> "_ _ _ _ _ _ _ _"
 +
  hangman.add( 'A' );
 +
  hangman.getHint()                  // --> "_ _ _ _ _ A _ _"
 +
  hangman.check( 'E' )              // --> true
 +
  hangman.add( 'E' );
 +
  hangman.getHint()                  // --> "E _ E _ _ A _ _"
 +
</syntaxhighlight>
 +
 +
=== More on JavaScript objects ===
 +
In the previous example, we create an object using object literal notation.  That syntax is very easy to use, but it is hard to create many objects of the same kind using that syntax.  In JavaScript, although it is a prototype language, we can define a new class using function.  Let's start by an example.
 +
 +
Previously if we want a dog object, we can do the following.
 +
 +
<syntaxhighlight lang="javascript">
 +
var dog = {
 +
    age: 10,
 +
    color: "black"
 +
};
 +
</syntaxhighlight>
 +
 +
If we want to have many objects that look like this dog, we can write a function to initialize a object for us as follows.
 +
 +
<syntaxhighlight lang="javascript">
 +
function initDog( dog, a, c ) {
 +
    dog.age = a;
 +
    dog.color = c;
 +
}
 +
 +
// new dogs
 +
var dang = {};
 +
var dum = {}
 +
initDog( dang, 5, 'red' );
 +
initDog( dum, 7, 'blue' );
 +
</syntaxhighlight>
 +
 +
Since creating and initializing objects are common things people want to do, the JavaScript language designers has provide a simpler syntax to do that.  The idea is very similar to the above example.
 +
 +
To create a class, we define a constructor function for the class.  This is pretty much the function <tt>initDog</tt> above.
 +
 +
<syntaxhighlight lang="javascript">
 +
function Dog( age, color ) {
 +
    this.age = a;
 +
    this.color = c;
 +
}
 +
</syntaxhighlight>
 +
 +
We will '''not''' use this function like a normal function.  To create an object, we use the '''<tt>new</tt>''' statement.  It is like we call the function, but with an additional keyword <tt>new</tt>.
 +
 +
<syntaxhighlight lang="javascript">
 +
// new dogs
 +
var dang = new Dog( 5, 'red' );
 +
var dum = new Dog( 7, 'blue' );
 +
</syntaxhighlight>
 +
 +
'''NOTES: Don't forget the <tt>new</tt> keyword.'''
 +
 +
==== Adding methods ====
 +
We can add a method to the object in the constructor function like this:
 +
 +
<syntaxhighlight lang="javascript">
 +
function sayhifunction() {
 +
    alert( "I am " + this.age );
 +
}
 +
function Dog( age, color ) {
 +
    this.age = age;
 +
    this.color = color;
 +
    this.sayhi = sayhifunction;
 +
}
 +
//----------
 +
var dang = new Dog( 5, 'red' );
 +
dang.sayhi();
 +
</syntaxhighlight>
 +
 +
The constructor above assigns a function to the object's property <tt>sayhi</tt>.  I.e., every object of class Dog has a property <tt>sayhi</tt> which is that function <tt>sayhifunction</tt>.  (I intentionally name that function <tt>sayhifunction</tt> so that it is different than <tt>sayhi</tt>.)
 +
 +
Note that in this case, we have to define function <tt>sayhifunction</tt> and we might pollute the name space in our program.  There is another way to add a method to a class.  It is through the class's '''prototype'''.
 +
 +
Let's add method <tt>walk</tt> to Dog's prototype:
 +
 +
<syntaxhighlight lang="javascript">
 +
Dog.prototype.walk = function() { alert( "I am walking." ); }
 +
 +
//-----
 +
dang.walk();            // now every object knows how to walk.
 +
</syntaxhighlight>
 +
 +
When you define a function, there is also a property of the function called '''<tt>prototype</tt>'''.  This is a "prototype" object where every object in the class would look for when it does not know how to perform any task.  The following figure shows the objects and its prototype.
 +
 +
[[Image:jsproto.png]]
 +
 +
==== Class pattern ====
 +
The following is a sample class Dog that we have written. 
 +
 +
<syntaxhighlight lang="javascript">
 +
function Dog( age, color ) {
 +
    this.age = age;
 +
    this.color = color;
 +
}
 +
 +
Dog.prototype.walk = function() {  // assign methods to the class's prototype
 +
    // ... do something, referring to instance object using the "this" keyword
 +
};
 +
 +
// to create an object and call its method.
 +
var green = new Dog( 5, 'black' );
 +
green.walk();
 +
</syntaxhighlight>
 +
 +
Note that here we prefer the prototype-based method assignment.  To see why, read [http://stackoverflow.com/questions/4305245/prototype-and-methods-which-one-is-preferred this] and [http://stackoverflow.com/questions/4508313/advantages-of-using-prototype-vs-defining-methods-straight-in-the-constructor this].
 +
 +
=== Step-by-step ===
 +
We will develop the '''<tt>HangmanWord</tt>''' class.  We will do this without developing the UI part of the game.  I'll outline the steps and you should try to work on it.
 +
 +
Let's create a new project and create a Git repository in that directory.
 +
 +
==== A class keeping the word ====
 +
Let's start with a class that keeps the word.
 +
 +
<syntaxhighlight lang="html5">
 +
<!doctype html>
 +
<html lang="en">
 +
<body>
 +
<script type="text/javascript">
 +
function HangmanWord( wordToGuess ) {
 +
    this.wordToGuess = wordToGuess;
 +
}
 +
</script>
 +
</body>
 +
</html>
 +
</syntaxhighlight>
 +
 +
Note that our page contains nothing.  So how can we test this code?  We'll use the JavaScript console like this:
 +
 +
[[Image:Jsconsole.png]]
 +
 +
{{gitcomment|If you can create a HangmanWord object, commit the first version.}}
 +
 +
==== Checking if an alphabet is in the word ====
 +
Let's add method '''<tt>check</tt>'''.
 +
 +
<syntaxhighlight lang="javascript">
 +
function HangmanWord( wordToGuess ) {
 +
    this.wordToGuess = wordToGuess;
 +
}
 +
HangmanWord.prototype.check = function( alphabet ) {
 +
    // ...your code here
 +
};
 +
</syntaxhighlight>
 +
 +
'''Exercise:''' Implement this method.
 +
 +
To do so, you need to use JavaScript String methods.  You can read the [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FString reference here].
 +
 +
Try to test the method in the JavaScript console.  Don't forget to reload the page before you test.
 +
 +
'''Notes:''' One question arises.  It might be good if the function works when the alphabet has the wrong case, e.g., if the word is 'ELEPHANT', <tt>check</tt> should return <tt>true</tt> even when alphabet is <tt>e</tt>.  Right now, let's try to be conservative, i.e., <tt>check</tt> works with only alphabet with the correct case.
 +
 +
{{gitcomment|Commit your work after it is done.}}
 +
 +
==== Getting the hint ====
 +
Let's add method <tt>getHint</tt>.
 +
 +
<syntaxhighlight lang="javascript">
 +
HangmanWord.prototype.getHint = function() {
 +
    // ...your code here
 +
};
 +
</syntaxhighlight>
 +
 +
In this step, we will implement <tt>getHint</tt> without worrying too much about the <tt>add</tt> method.
 +
 +
{{gitcomment|Commit this work after you have tested the method.}}
 +
 +
==== Adding a new alphabet to the hint ====
 +
Let's add method <tt>add</tt>.  This method itself does not work along.  You have to make sure that after you add new alphabet to the hint, method <tt>getHint</tt> gives the correct hint.
 +
 +
<syntaxhighlight lang="javascript">
 +
HangmanWord.prototype.add = function( alphabet ) {
 +
    // ...your code here
 +
};
 +
</syntaxhighlight>
 +
 +
Try to test the whole class thoroughly.  Try with a few word-to-guess's.
 +
 +
'''Hints:''' There are many ways to implement <tt>add</tt> and <tt>getHint</tt> but these two methods should work together.  It might be useful if you know JavaScript's [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FArray array].
 +
 +
{{gitcomment|Commit your work.}}
 +
 +
== Exercise: Hangman without the hanging man ==
 +
Use the <tt>HangmanWord</tt> class to implement the game. 
 +
 +
At this point, let's make a game with only text, and only keep the number of wrong guesses.  Also, you can just make a game with a fixed word (e.g., ELEPHANT, PARTY, etc).
 +
 +
[[Image:Jshangmantext.png]]
 +
 +
Try to work in small steps.
 +
 +
{{gitcomment|Don't forget to commit your work after you get one unit of work done.}}
 +
 +
=== Choosing the word randomly ===
 +
 +
Let's pick a few words so that our game does not get too boring.  Put the words in the array and randomly choose one word from the array using <tt>Math.random</tt> function.
  
 
== Assets ==
 
== Assets ==
 +
Let's try to add the figures to our game.  To do that, we will create a few images, which are usually referred to as assets.
 +
 +
=== Creating images ===
 +
We will need 6 (or 7) images for the hangman (depending on the number of mistakes you allow).  Use any paint/image processing programs to create these images.  Save them as '''<tt>hangman1.png</tt>''', '''<tt>hangman2.png</tt>''', and so on.
 +
 +
We should be a bit organized with our assets, so let's put these image files in <tt>images</tt> sub-directory of our project.
 +
 +
=== Show all images ===
 +
The following HTML code shows images 1 - 3.
 +
 +
<syntaxhighlight lang="html5">
 +
<img src="images/hangman1.png">
 +
<img src="images/hangman2.png">
 +
<img src="images/hangman3.png">
 +
</syntaxhighlight>
 +
 +
'''Exercise:''' Show all of your images.
 +
 +
{{gitcomment|Commit your work.  Don't forget to add image files.}}
 +
 +
=== Manipulation ===
 +
We can use jQuery to manipulate the visibilities of these images.  To do so, we have to add the <tt>id</tt>'s to the images, like:
 +
 +
<img id="hangman1" src="images/hangman1.png">
 +
 +
jQuery provide <tt>hide</tt> and <tt>show</tt> method that you can apply to the jQuery objects to show and hide them.  The following is an example:
 +
 +
<syntaxhighlight lang="javascript">
 +
$("#hangman1").hide();    // hide this image
 +
$("#hangman1").show();    // show this image
 +
</syntaxhighlight>
 +
 +
=== Exercise: complete the game ===
 +
 +
You have all the ingredients to complete the Hangman game.  Let's do it.
 +
 +
{{gitcomment|You are done. Don't forget to commit your work.}}

รุ่นแก้ไขปัจจุบันเมื่อ 06:42, 29 มกราคม 2557

This is part of 01219245.

Everyone should know what Hangman Game is. If you don't, it's a simple vocabulary game and you can try it here.

Task breakdown

Exercise: Let's think about what our Hangman program has do to.

Expand this box to see a few examples after you finish thinking.

  • Random a word.
  • Show the word with blanks.
  • Read the guess alphabets and show the correct ones in the word.
  • Count the number of wrong guesses.
  • Show the hangman figures.
  • Show a list of alphabets from which the user can choose.
  • Hide used alphabets.
  • Reset the game after the game ends.
  • (... I can keep listing other features.)

Not all features above are extremely important to the game. For example, if you don't even draw the hangman figures, the game will be a pretty weird hangman game, but it is still the hangman game. If we can only choose 2 features to implement, we can simply throw away the hangman drawing task.

Exercise: From the list above, try to order the items according to their importance to the game.

Expand this box to see some possible ordering.

This is how I would order it. It is OK if your preference is different.

  • Show the word with blanks.
  • Read the guess alphabets and show the correct ones in the word.
  • Count the number of wrong guesses.
  • Random a word.
  • Show the hangman figures.
  • Show a list of alphabets from which the user can choose.
  • Hide used alphabets.
  • Reset the game after the game ends.

Core mechanics

Notice that the first few most important features are related to game mechanics. Let's try to think about that part, i.e., what is the essence of the game?

To see that, let's try to think about a sample play between A and B:

  • A: I have this word _ _ _ _ _.
  • B: A?
  • A: No, I don't have A. This is your first wrong guess.
  • B: E?
  • A: Yes I have E. The word is now: _ E _ _ _.
  • B: I?
  • A: No, I don't have I. This is your second wrong guess.
  • B: O?
  • A: Yes I have O. The word is now: _ E _ _ O.

What do we see here? Do you see any data? Do you see any "behavior" or "operations with data"? This is a nice indicator that we can group the data and functions related to this piece of data into an object.

Let's call it, for now, a hangman word and think about the operations we would like to perform with it.

Hangman word:

  • We want to check if the word contains an alphabet.
  • We want to get hints (e.g., something like "_ E _ _ O")

To do so, we should create the hangman word object with the "hidden word", i.e., hangman word should know the word to guess.

With this in mind, we can think about the object's interface, i.e., how we should interact with the object. Let's think about this in a code example.

  var hangman = new HangmanWord( 'ELEPHANT' );       // how a new object is created.
  
  // Sample interaction in the JavaScript console
  hangman.getHint()                  // --> "_ _ _ _ _ _ _ _"
  hangman.check( 'Z' )               // --> false
  hangman.check( 'A' )               // --> true
  hangman.getHint()                  // --> "_ _ _ _ _ _ _ _"
  hangman.add( 'A' );
  hangman.getHint()                  // --> "_ _ _ _ _ A _ _"
  hangman.check( 'E' )               // --> true
  hangman.add( 'E' );
  hangman.getHint()                  // --> "E _ E _ _ A _ _"

More on JavaScript objects

In the previous example, we create an object using object literal notation. That syntax is very easy to use, but it is hard to create many objects of the same kind using that syntax. In JavaScript, although it is a prototype language, we can define a new class using function. Let's start by an example.

Previously if we want a dog object, we can do the following.

var dog = {
    age: 10,
    color: "black"
};

If we want to have many objects that look like this dog, we can write a function to initialize a object for us as follows.

function initDog( dog, a, c ) {
    dog.age = a;
    dog.color = c;
}

// new dogs
var dang = {};
var dum = {}
initDog( dang, 5, 'red' );
initDog( dum, 7, 'blue' );

Since creating and initializing objects are common things people want to do, the JavaScript language designers has provide a simpler syntax to do that. The idea is very similar to the above example.

To create a class, we define a constructor function for the class. This is pretty much the function initDog above.

function Dog( age, color ) {
    this.age = a;
    this.color = c;
}

We will not use this function like a normal function. To create an object, we use the new statement. It is like we call the function, but with an additional keyword new.

// new dogs
var dang = new Dog( 5, 'red' );
var dum = new Dog( 7, 'blue' );

NOTES: Don't forget the new keyword.

Adding methods

We can add a method to the object in the constructor function like this:

function sayhifunction() {
    alert( "I am " + this.age );
}
function Dog( age, color ) {
    this.age = age;
    this.color = color;
    this.sayhi = sayhifunction;
}
//----------
var dang = new Dog( 5, 'red' );
dang.sayhi();

The constructor above assigns a function to the object's property sayhi. I.e., every object of class Dog has a property sayhi which is that function sayhifunction. (I intentionally name that function sayhifunction so that it is different than sayhi.)

Note that in this case, we have to define function sayhifunction and we might pollute the name space in our program. There is another way to add a method to a class. It is through the class's prototype.

Let's add method walk to Dog's prototype:

Dog.prototype.walk = function() { alert( "I am walking." ); }

//-----
dang.walk();            // now every object knows how to walk.

When you define a function, there is also a property of the function called prototype. This is a "prototype" object where every object in the class would look for when it does not know how to perform any task. The following figure shows the objects and its prototype.

Jsproto.png

Class pattern

The following is a sample class Dog that we have written.

function Dog( age, color ) {
    this.age = age;
    this.color = color;
}

Dog.prototype.walk = function() {   // assign methods to the class's prototype
    // ... do something, referring to instance object using the "this" keyword
};

// to create an object and call its method.
var green = new Dog( 5, 'black' );
green.walk();

Note that here we prefer the prototype-based method assignment. To see why, read this and this.

Step-by-step

We will develop the HangmanWord class. We will do this without developing the UI part of the game. I'll outline the steps and you should try to work on it.

Let's create a new project and create a Git repository in that directory.

A class keeping the word

Let's start with a class that keeps the word.

<!doctype html>
<html lang="en">
<body>
<script type="text/javascript">
function HangmanWord( wordToGuess ) {
    this.wordToGuess = wordToGuess;
}
</script>
</body>
</html>

Note that our page contains nothing. So how can we test this code? We'll use the JavaScript console like this:

Jsconsole.png

Gitmark.png If you can create a HangmanWord object, commit the first version.

Checking if an alphabet is in the word

Let's add method check.

function HangmanWord( wordToGuess ) {
    this.wordToGuess = wordToGuess;
}
HangmanWord.prototype.check = function( alphabet ) {
    // ...your code here
};

Exercise: Implement this method.

To do so, you need to use JavaScript String methods. You can read the reference here.

Try to test the method in the JavaScript console. Don't forget to reload the page before you test.

Notes: One question arises. It might be good if the function works when the alphabet has the wrong case, e.g., if the word is 'ELEPHANT', check should return true even when alphabet is e. Right now, let's try to be conservative, i.e., check works with only alphabet with the correct case.

Gitmark.png Commit your work after it is done.

Getting the hint

Let's add method getHint.

HangmanWord.prototype.getHint = function() {
    // ...your code here
};

In this step, we will implement getHint without worrying too much about the add method.

Gitmark.png Commit this work after you have tested the method.

Adding a new alphabet to the hint

Let's add method add. This method itself does not work along. You have to make sure that after you add new alphabet to the hint, method getHint gives the correct hint.

HangmanWord.prototype.add = function( alphabet ) {
    // ...your code here
};

Try to test the whole class thoroughly. Try with a few word-to-guess's.

Hints: There are many ways to implement add and getHint but these two methods should work together. It might be useful if you know JavaScript's array.

Gitmark.png Commit your work.

Exercise: Hangman without the hanging man

Use the HangmanWord class to implement the game.

At this point, let's make a game with only text, and only keep the number of wrong guesses. Also, you can just make a game with a fixed word (e.g., ELEPHANT, PARTY, etc).

Jshangmantext.png

Try to work in small steps.

Gitmark.png Don't forget to commit your work after you get one unit of work done.

Choosing the word randomly

Let's pick a few words so that our game does not get too boring. Put the words in the array and randomly choose one word from the array using Math.random function.

Assets

Let's try to add the figures to our game. To do that, we will create a few images, which are usually referred to as assets.

Creating images

We will need 6 (or 7) images for the hangman (depending on the number of mistakes you allow). Use any paint/image processing programs to create these images. Save them as hangman1.png, hangman2.png, and so on.

We should be a bit organized with our assets, so let's put these image files in images sub-directory of our project.

Show all images

The following HTML code shows images 1 - 3.

<img src="images/hangman1.png">
<img src="images/hangman2.png">
<img src="images/hangman3.png">

Exercise: Show all of your images.

Gitmark.png Commit your work. Don't forget to add image files.

Manipulation

We can use jQuery to manipulate the visibilities of these images. To do so, we have to add the id's to the images, like:

<img id="hangman1" src="images/hangman1.png">

jQuery provide hide and show method that you can apply to the jQuery objects to show and hide them. The following is an example:

$("#hangman1").hide();    // hide this image
$("#hangman1").show();    // show this image

Exercise: complete the game

You have all the ingredients to complete the Hangman game. Let's do it.

Gitmark.png You are done. Don't forget to commit your work.