Difference between revisions of "Javascript1"
Line 655: | Line 655: | ||
|} | |} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | =====Object creation syntax:===== | |
+ | * using ''new'' operator: | ||
+ | var empty=new Object(); // empty object | ||
+ | |||
+ | var rect=new Object(); //empty object | ||
+ | rect.shape= "rectangle"; //set a property | ||
+ | rect.numsides=4; //set another property | ||
+ | rect.blank= new Object(); //set another property, an nested empty object | ||
+ | |||
+ | * using object-literal notation (JSON): | ||
+ | var obj = {}; //empty object | ||
+ | |||
+ | var rect = {shape:"rectangle", numsides:4, blank:{}}; // object with 3 initial properties | ||
+ | |||
+ | =====Property syntax:===== | ||
+ | * using the ''dot'' operator: | ||
+ | rect.numsides; //4 | ||
+ | rect.blank.innerProp; //undefined | ||
+ | |||
+ | * using the [] (''member'') operator: | ||
+ | rect["numsides"];//4 | ||
+ | rect[numsides]; //Reference Error (unless numsides is a variable) | ||
+ | |||
{| class="wikitable" style="text-align:center;" | {| class="wikitable" style="text-align:center;" | ||
+ | |+Some object-related operators: | ||
! operator | ! operator | ||
! input type | ! input type | ||
Line 697: | Line 713: | ||
(true if found, else false) | (true if found, else false) | ||
| removes property | | removes property | ||
+ | |- | ||
+ | | new _ | ||
+ | | constructor | ||
+ | | var obj = new Object(); | ||
+ | | an object | ||
+ | | OBJ | ||
+ | | allocates object space | ||
|- | |- | ||
| _ in _ | | _ in _ | ||
Line 704: | Line 727: | ||
|BOOL | |BOOL | ||
|} | |} | ||
+ | |||
+ | Methods: | ||
+ | * are functions which are properties of an object. | ||
+ | * use the keyword ''this'' to refer to their object (not themselves) | ||
+ | * Examples: | ||
+ | ** Math.sqrt() | ||
+ | ** Math.floor() | ||
+ | ** Number.isInteger() | ||
====Arrays==== | ====Arrays==== | ||
− | An array is an object | + | An array is an object containing a series of integer-indexed values (i.e. properties whose names are integers). |
+ | * Indexes always begin with 0 (i.e. the first element has index 0) | ||
+ | * Therefore, the last element always has an index of (length-1) | ||
+ | |||
+ | A "pseudo-array" is any object with some properties named by integers: | ||
+ | <pre> | ||
+ | var arr={0:'first', 1:'second', 3:'fourth', length:4};//third is implicit but undefined | ||
+ | </pre> | ||
+ | * a length property is optional and must be set manually | ||
+ | |||
+ | A genuine ''Array'' is a special class of object which... | ||
+ | <ul> | ||
+ | <li>automatically coordinates its integer-named properties with a special ''length'' property. | ||
+ | <ul><li>When creating new properties with index >= length, length extends to include it: | ||
+ | <pre> | ||
+ | var arr=['a','b','c']; | ||
+ | arr.length; //3 | ||
+ | arr[9]='j'; | ||
+ | arr.length; //10 | ||
+ | </pre> | ||
+ | <li> When setting length to a greater value, new properties (with value undefined) fill in the end | ||
+ | <li> When setting length to a lesser value, properties beyond that new range are deleted | ||
+ | </ul> | ||
+ | <li> has a predefined set of useful methods: | ||
+ | <ul><li> push(VAL): modifies array, adding VAL to the end | ||
+ | <li> pop(): modifies array, removing last value and returning it | ||
+ | <li> join(DELIM): creates string by concatenating array elements with DELIM between them. | ||
+ | The converse of join is String.split(DELIM), which divides a string and creates an array. | ||
+ | <li> many more... | ||
+ | </ul></ul> | ||
+ | |||
+ | =====Array-creation syntax===== | ||
+ | var arr=new Array(); //empty array | ||
+ | var arr = []; //empty array | ||
+ | var arr = [1,2,3,"four",{},[]]; //array with six elements, including an empty Object and empty Array | ||
− | + | ====Wrapper Objects==== | |
− | + | Primitives may be manually or automatically wrapped in type-specific objects which allow them properties and built-in methods: | |
+ | <ul> | ||
+ | <li> Numbers: | ||
+ | var four= Object(4); //number wrapper object | ||
+ | typeof four;//"object" | ||
+ | four== 4;//true | ||
+ | four===4;//false | ||
+ | four.valueOf();//4 | ||
+ | four*3; //12 | ||
+ | <li> Strings: | ||
+ | var xyz= Object("x,y,z"); //string wrapper object | ||
+ | xyz== "x,y,z";//true | ||
+ | xyz==="x,y,z";//false | ||
+ | xyz.length;//5 | ||
+ | xyz[2];//"y" | ||
+ | xyz.split(",");//array ["x","y","z"] | ||
− | + | "a b c".split(" ");//array ["a","b","c"] (first auto-converts "a b c" to wrapper obj) | |
− | + | </ul> | |
− | |||
− | |||
− | |||
− | |||
− | |||
====Demo Code==== | ====Demo Code==== |
Revision as of 19:34, 20 November 2013
General Information
This course is currently running November-December 2013, Tuesdays 11:30-1:30.
Part 1 (November) will offer students a solid foundation in Javascript as a general-purpose programming language. Part 2 (December) will explore Javascript's integration in web browsers with HTML, CSS, and the Document Object Model (DOM).
Instructor: Dan Bauer (email: dsbauer at gmail)
Course Outline
Week 1: Values and Expressions
- Firebug Console/Interpreter
- The console allows a dialogue with Javascript interpreter: you make one (possibly compound) statement, it replies with one value.
- Expressions
- An expression (EXPR) can be:
- a primitive (e.g. 1)
- a variable (e.g. x)
- an operator combining smaller EXPR's (e.g. x+1, 2*(x+1))
- An expression (EXPR) can be:
- Operators have:
- a keyword or symbol (OP)
- a syntax template (e.g. _ OP _)
- unary prefix: OP_
- unary postfix: _OP
- binary infix: _OP_
- ternary infix: _OP_OP_
- one or more inputs _ ("operands")
- one output ("return value")
- may be undefined
- possible side-effects (e.g. change in variable's value)
- Variables have:
- a name/identifier
- a value (initially undefined)
- each value has a type (primitive or reference)
- a scope
- Other (non-expression) Statements
- var NAME;
- var NAME = EXPR;
- if (EXPR) {EXPR;}
- if (EXPR) {EXPR;} else {EXPR;}
- Primitive Types
- undefined
- Number
- same type for integers and floats (fractional)
- special values: NaN ("not a number"), Infinity
- String
- wrapped in either " " or ' '
- may contain opposite quote or other special characters
- \t (tab)
- \n (newline)
- may be empty ("")
- Boolean (true/false)
- false-ish values: false,undefined,null,0,"",NaN
- (some) true-ish values: true,1,"false","undefined","0",{} (empty object)
Some Sample Operators:
operator | input type | example | result | result type | side effects |
---|---|---|---|---|---|
typeof | NUM | typeof 1 | "number" | STR | |
STR | typeof "word" | "string" | |||
BOOL | typeof true | "boolean" | |||
,
(sequence) |
any | "a", "b", "c" | "c" | type of
last value | |
(4,"3"),("two",1) | 1 | ||||
+
(plus) |
NUM | 1+1 | 2 | NUM | |
+
(concatenate) |
STR | "1"+"1" | "11" | STR | |
mixed | 1+"2" | "12" | STR | ||
- * / % | NUM | 3-1 | 2 | NUM | |
STR | "3"-"1" | 2 | |||
mixed | "3"-1 | 2 | |||
< > <= >=
(comparison) |
NUM | 1<2 | true | BOOL | |
STR | "b"<"a" | false | |||
==
(equality) |
NUM | 1==2 | false | BOOL | |
STR | "a"=="aa" | false | |||
mixed | 1=="1" | true | |||
mixed | 0==false | true | |||
===
(identity) |
NUM | 1===2 | false | BOOL | |
STR | "abc"==="abc" | true | |||
mixed | 1==="1" | false | |||
mixed | 1===true | false | |||
!
(not) |
any | !1 | false | BOOL | |
!"" | true | ||||
&&
(and) |
any | null && "red" | null | first false-ish
or last input | |
1 && "green" | "green" | ||||
||
(or) |
any | "yes" || null | "yes" | first true-ish
or last input | |
"" || 0 | 0 | ||||
?:
(conditional) |
any | true? "yes": "no" | "yes" | any | |
variable assignment operators | x is now... | ||||
=
(assignment) |
any | x=1 | 1 | same as input | 1 |
x = "blue" | "blue" | "blue" | |||
+=
(incremental assignment) |
NUM | x=0; x+=2; | 2 | NUM | 2 |
STR | x="he"; x+="llo"; | "hello" | STR | "hello" | |
mixed | x=""; x+=2; | "2" | STR | "2" | |
++
(post-increment) |
NUM | x=0; x++; | 0 | NUM | 1 |
STR | x=""; x++; | 0 | 1 | ||
++
(pre-increment) |
NUM | x=0; ++x; | 1 | NUM | 1 |
STR | x=""; ++x; | 1 | 1 |
Week 2: Loops and Functions
Nov.12 in-class scratchpad
/* * This is a JavaScript Scratchpad. * * Enter some JavaScript, then Right Click or choose from the Execute Menu: * 1. Run to evaluate the selected text (Ctrl+R), * 2. Inspect to bring up an Object Inspector on the result (Ctrl+I), or, * 3. Display to insert the result in a comment after the selection. (Ctrl+L) */ //function countdown() { var countdown = function (start) { var list=""; // generate string of numbers from 10 to 0 for (var i=start; i>0; i--) { list += (i + " "); } return list; } countdown var alias=countdown; alias(7); alias(8); /* 8 7 6 5 4 3 2 1 *//* 7 6 5 4 3 2 1 */ alias = 7; /* 10 9 8 7 6 5 4 3 2 1 */ /* function countdown() { var list=""; // generate string of numbers from 10 to 0 for (var i=10; i>0; i--) { list += (i + " "); } } */ countdown(); /* 10 9 8 7 6 5 4 3 2 1 */ /* 10 9 8 7 6 5 4 3 2 1 */ /* undefined */ function countdown() {} function plus(x,y) { return x+y; } plus(1,2); /* 3 */
Loops have:
- some form of counter ("iterator"), a variable which changes with each cycle of the loop
- an initialization (INIT)
- a counter increment/decrement (CHANGE)
- a repeated ACTION
- a continuation condition (COND)
- any COND expression will be treated as a Boolean, either true-ish or false-ish
- the ACTIONs and CHANGE occur only when COND is true-ish
Loop Statements:
Loop keyword | Pattern | Example |
---|---|---|
while |
INIT; while(COND) { ACTION; ... CHANGE; } |
var i=0; while (i<10) { alert(i); i++; } |
for |
for (INIT; COND; CHANGE) { ACTION; ... } |
for (var i=0; i<10; i++) { alert(i); } |
- Sometimes the COND includes the CHANGE, e.g.:
for (var i=0; (i++)<10; ) {...} for (var i=10; --i; ) {...}
- ACTIONs can be any statements, including nested loops
Functions:
- are mini-programs, independent modules which can be reused in multiple contexts
- are a kind of Object, which can be referenced by multiple names
- have parameters which can change for each call/use
- return one value of any type (or undefined if unspecified)
- the keyword return specifies the returned value and exits the function immediately
- create a separate space ("call object","execution context", or "scope") for their local variables and parameters
- Each call creates a separate scope.
- Scopes last as long as needed, then destroyed automatically.
Function-specific Operators:
operator | input type | example | result | result type | side effects |
---|---|---|---|---|---|
function NAME(PARAMS){BODY} function(PARAMS){BODY} (define function) |
function add(x,y) {return x+y;} | a function | Function | creates Function object,
creates (or redefines) var. NAME if included | |
NAME(ARGS) (call function) |
NAME: Function,
ARGS: any |
add(1,2) | 3 | any | depends on function body |
Exercises
1) Write a function to calculate, for a group of N people where everyone shakes the hand of everyone else, how many total handshakes there are.
Solution...
function numPairs(setSize) { return setSize*(setSize-1)/2; }
Write another function to enumerate these interactions, returning a single string describing them all. People may be identified by number. For example, for N=3, it might return: "#1 meets #2. #1 meets #3. #2 meets #3."
(Hint: you may want a second "helper" function to handle a single interaction.)
Solution...
function writePair(personA,personB) { return "#"+personA+" meets #"+personB+". "; } function enumeratePairs(numPeople) { var result=""; for (var personA=1; personA<=numPeople; personA++) { for (var personB=personA+1; personB<=numPeople; personB++) { result+=writePair(personA,personB); } } return result; }
2) Write a function to decide whether a given integer is prime. You may need some of these functions/operators:
- function Math.floor(N): truncates any fractional part of a number N (i.e. returns greatest int <=N)
- function Number.isInteger(N): returns true is N is an integer
- modulo operator %: x%y returns 0 if x divides evenly by y
Solution...
function isPrime(n) { if (n==0 || n==1 || !Number.isInteger(n)) return false; // excludes 0,1,and non-integers // search for number which divides n evenly... var sqrt=Math.sqrt(n); // only need to search up to n's square root for (var test=2; test<=sqrt; test++) { if (Number.isInteger(n/test)) return false; //found one; n isn't prime } return true;//n must be prime }
3) Imagine that a deck of playing cards is sorted by rank and suit: first all the Aces, then the Twos, etc, with the Kings last. Within each rank, the suits are in the order Heart, Diamond, Spade, Club. Number each card in order from 0 to 51 (i.e. 0=Ace of Hearts; 51=King of Clubs), and let that ID number represent the corresponding card.
Following that encoding scheme, write a set of functions to compute different features/relations of the cards:
- rank(card) returns 1-13, representing the card's rank.
- suit(card) returns 1-4, representing the card's suit.
- cardNum(rank,suit) returns 0-51, identifying the card of a given rank and suit.
- color(card) returns "red" or "black".
- precedes(cardA,cardB) returns true only if cardA is one less in rank than cardB. Assume rank wrap-around (King precedes Ace precedes Two).
- sameColor(cardA,cardB) returns true only if cardA and cardB have the same color.
- nextInSuit(cardA) returns cardB, the ID number of the card following cardA in the same suit. Assume wrap-around.
- prevInSuit(cardB) returns cardA, the ID number of the card preceding cardB in the same suit.
Your functions may call each other. Try to reuse their functionality to avoid duplicating code.
Initially, assume each function is given valid arguments (i.e. the args are integers in the appropriate range). Later, devise a system for dealing with arguments which are invalid in various ways, and rewrite your functions to tolerate such errors whenever possible.
Solution: simple version...
function rank(card) { // --> 1..13 return Math.floor(card/4)+1; } function suit(card) { // --> 1..4 return (card%4)+1; } function cardNum(rank,suit) { // --> 0..51 return (rank-1)*4 + (suit-1); } function color(card) { // -->"red,"black" var theSuit=suit(card); return (theSuit<3)? "red": "black"; } function precedes(cardA,cardB) { //-->false,true var diff= rank(cardB)-rank(cardA); return diff==1 || diff==-12; } function sameColor(cardA,cardB) { //-->false,true return color(cardA)==color(cardB); } function nextInSuit(cardA) {//--> 0..51 nextCard = cardA+4; if (nextCard>51) nextCard-=52; return nextCard; } function prevInSuit(cardB) {//--> 0..51 prevCard = cardB-4; if (prevCard<0) prevCard+=52; return prevCard; }
Solution: error-detecting version...
// Helper function; ensures num is integer between low, high // Returns true if valid, otherwise NaN function isValid(num,low,high) { // Possible return values--> NaN, true if ((typeof num)!="number") //wrong type return NaN; if (!Number.isInteger(num)) //non-integer return NaN; if (num<low || num>high) //out of range return NaN; return true; } function rank(card) { // --> 1..13, NaN return isValid(card,0,51) && Math.floor(card/4)+1; } function suit(card) { // --> 1..4, NaN return isValid(card,0,51) && (card%4)+1; } function cardNum(rank,suit) { // --> 0..51, NaN return isValid(rank,1,13) && isValid(suit,1,4) && ((rank-1)*4 + (suit-1)); } function color(card) { // -->"red,"black",NaN var theSuit=suit(card); if (isNaN(theSuit)) return NaN; return (theSuit<3)? "red": "black"; } function precedes(cardA,cardB) { //-->false,true,NaN var diff= rank(cardB)-rank(cardA); if (isNaN(diff)) return NaN; return diff==1 || diff==-12; } function sameColor(cardA,cardB) { //-->false,true,NaN var colorA=color(cardA), colorB=color(cardB); if (Number.isNaN(colorA) || Number.isNaN(colorB)) // must use Number.isNaN() instead of isNaN(), which returns true for non-numeric strings return NaN; return colorA==colorB; } function nextInSuit(cardA) {//--> 0..51,NaN nextCard = isValid(cardA,0,51) && cardA+4; if (nextCard>51) nextCard-=52; return nextCard; } function prevInSuit(cardB) {//--> 0..51,NaN prevCard = isValid(cardB,0,51) && cardB-4; if (prevCard<0) prevCard+=52; return prevCard; }
Week 3: Objects, Arrays, and Methods
Under construction; watch this space...
Objects
An object is a collection of named "properties" or "members", similar to variables...
Variables... | Properties... |
---|---|
live in a global or function "scope" (execution context) | live in an object |
need no prefix | are (usually) prefixed with their object and an operator (. or []) |
must be declared with var | are created merely by assigment |
cause an error if not declared | return undefined if not declared |
have naming restrictions | may be named with any string |
last until their scope expires | may be deleted |
Object creation syntax:
- using new operator:
var empty=new Object(); // empty object
var rect=new Object(); //empty object rect.shape= "rectangle"; //set a property rect.numsides=4; //set another property rect.blank= new Object(); //set another property, an nested empty object
- using object-literal notation (JSON):
var obj = {}; //empty object
var rect = {shape:"rectangle", numsides:4, blank:{}}; // object with 3 initial properties
Property syntax:
- using the dot operator:
rect.numsides; //4 rect.blank.innerProp; //undefined
- using the [] (member) operator:
rect["numsides"];//4 rect[numsides]; //Reference Error (unless numsides is a variable)
operator | input type | example | result | result type | side effect |
---|---|---|---|---|---|
var obj={name:"Bubba"} | |||||
_._
(dot) |
OBJ,PROP | obj.name | "Bubba" | any | |
_[_]
(index/member/"sub") |
OBJ,STR | obj["name"] | "Bubba" | any | |
delete _._ | OBJ,PROP | delete obj.name | true | BOOL
(true if found, else false) |
removes property |
new _ | constructor | var obj = new Object(); | an object | OBJ | allocates object space |
_ in _ | STR,OBJ | ("name" in obj) | true | BOOL |
Methods:
- are functions which are properties of an object.
- use the keyword this to refer to their object (not themselves)
- Examples:
- Math.sqrt()
- Math.floor()
- Number.isInteger()
Arrays
An array is an object containing a series of integer-indexed values (i.e. properties whose names are integers).
- Indexes always begin with 0 (i.e. the first element has index 0)
- Therefore, the last element always has an index of (length-1)
A "pseudo-array" is any object with some properties named by integers:
var arr={0:'first', 1:'second', 3:'fourth', length:4};//third is implicit but undefined
- a length property is optional and must be set manually
A genuine Array is a special class of object which...
- automatically coordinates its integer-named properties with a special length property.
- When creating new properties with index >= length, length extends to include it:
var arr=['a','b','c']; arr.length; //3 arr[9]='j'; arr.length; //10
- When setting length to a greater value, new properties (with value undefined) fill in the end
- When setting length to a lesser value, properties beyond that new range are deleted
- When creating new properties with index >= length, length extends to include it:
- has a predefined set of useful methods:
- push(VAL): modifies array, adding VAL to the end
- pop(): modifies array, removing last value and returning it
- join(DELIM): creates string by concatenating array elements with DELIM between them. The converse of join is String.split(DELIM), which divides a string and creates an array.
- many more...
Array-creation syntax
var arr=new Array(); //empty array var arr = []; //empty array var arr = [1,2,3,"four",{},[]]; //array with six elements, including an empty Object and empty Array
Wrapper Objects
Primitives may be manually or automatically wrapped in type-specific objects which allow them properties and built-in methods:
- Numbers: var four= Object(4); //number wrapper object typeof four;//"object" four== 4;//true four===4;//false four.valueOf();//4 four*3; //12
- Strings: var xyz= Object("x,y,z"); //string wrapper object xyz== "x,y,z";//true xyz==="x,y,z";//false xyz.length;//5 xyz[2];//"y" xyz.split(",");//array ["x","y","z"] "a b c".split(" ");//array ["a","b","c"] (first auto-converts "a b c" to wrapper obj)
Demo Code
Version 0: sample from exercise #3
//===== Version 0: Global functions with card argument (from Exercise #3)
function rank(cardnum) { return Math.floor(cardnum/4)+1;// --> 1..13 } function suit(cardnum) { return cardnum%4+1; // --> 1..4 } function color(cardnum) { return (suit(cardnum)<3)? "red":"black"; } //etc...
//Testing: rank(51); // 13 (King) suit(51); // 4 (Clubs) color(51); // "black"
Version 1: adding card names
//===== Version 1: Add card names
function rank(cardnum) { return Math.floor(cardnum/4)+1;// --> 1..13 } function suit(cardnum) { return cardnum%4+1; // --> 1..4 } function color(cardnum) { return (suit(cardnum)<3)? "red":"black"; } //etc...
function name(cardnum) { return rankNames[rank(cardnum)] + " of " + suitNames[suit(cardnum)]; }
var rankNames = ["","Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"]; var suitNames = ["","Hearts","Diamonds","Spades","Clubs"];
//Testing: rank(51); //13 suit(51); //4 color(51); // "black" name(51); // King of Clubs
Version 2a: single controller object
//======== Version 2: Methods of single object var cardReader = {}; // "toolkit" object containing all card functions
cardReader.rank = function(cardnum) { return Math.floor(cardnum/4)+1;// --> 1..13 } cardReader.suit = function(cardnum) { return cardnum%4+1; // --> 1..4 } cardReader.color = function(cardnum) { return (this.suit(cardnum)<3)? "red":"black"; }
cardReader.name = function(cardnum) { return this.rankNames[this.rank(cardnum)] +" of "+ this.suitNames[this.suit(cardnum)]; }
cardReader.rankNames = ["","Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"]; cardReader.suitNames = ["","Hearts","Diamonds","Spades","Clubs"];
//Testing: cardReader.rank(51); //13 cardReader.suit(51); //4 cardReader.color(51); // "black" cardReader.name(51); // King of Clubs
Version 2b: single controller object (alternate form)
//======== Version 2b: Methods of single object (object literal/JSON form) var cardReader = { // "toolkit" object containing all card functions
rank: function(cardnum) { return Math.floor(cardnum/4)+1;// --> 1..13 }, suit: function(cardnum) { return cardnum%4+1; // --> 1..4 }, color: function(cardnum) { return (this.suit(cardnum)<3)? "red":"black"; },
name: function(cardnum) { return this.rankNames[this.rank(cardnum)] +" of "+ this.suitNames[this.suit(cardnum)]; },
rankNames: ["","Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"], suitNames: ["","Hearts","Diamonds","Spades","Clubs"] }; //end cardReader
//Testing: cardReader.rank(51); //13 cardReader.suit(51); //4 cardReader.color(51); // "black" cardReader.name(51); // King of Clubs
Version 3: single maker object, multiple instance objects
//===== Version 3: Card as object (w. own methods & cached data fields) // Each card is now an object, created by function makeCard function makeCard(cardnum) { var card = {};
//computed initially: card.rank = Math.floor(cardnum/4)+1; card.suit = cardnum%4+1;
//deferred: card.color = function() { return (this.suit<3)? "red":"black"; }
card.name = function() { return (rankNames[this.rank] +" of "+ suitNames[this.suit]); }
var rankNames = ["","Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"]; var suitNames = ["","Hearts","Diamonds","Spades","Clubs"];
return card; }
//Testing: var card51 = makeCard(51); card51.rank; //13 card51.suit; //4 card51.color(); //"black" card51.name(); //King of Clubs
Version 4: multiple instance objects sharing arrays
//===== Version 4: Card as object (as before), but arrays are shared
function makeCard(cardnum) { var card = {};
//computed initially: card.rank = Math.floor(cardnum/4)+1; card.suit = cardnum%4+1;
//deferred: card.color = function() { return (this.suit<3)? "red":"black"; }
card.name = function() { return (makeCard.rankNames[this.rank] +" of "+ makeCard.suitNames[this.suit]); }
return card; }
//Arrays created AFTER function, attached to it: makeCard.rankNames = ["","Ace","Two","Three","Four","Five","Six","Seven","Eight","Nine","Ten","Jack","Queen","King"]; makeCard.suitNames = ["","Hearts","Diamonds","Spades","Clubs"];
//Testing: var card51 = makeCard(51); card51.rank; //13 card51.suit; //4 card51.color(); //"black" card51.name(); //King of Clubs