Using Object Methods Prototype defines quite a few methods on Object, the generic constructor and patriarch of the JavaScript family.. The one other value returned by typeofis function,
Trang 1}
});
var player = new Player("Johnny", "Generic");
Object.toJSON(player);
//-> '{ "firstName": "Johnny", "lastName": "Generic", "points": 0 }'
var qb = new Quarterback("Andrew", "Dupont");
Object.toJSON(qb);
//-> '{ "position": "QB", "firstName": "Andrew",
//-> "lastName": "Dupont", "points": 0 }'
You need only define a toJSONmethod to tell your object how to encode itself into JSON
The preceding example illustrates one of JSON’s drawbacks: it isn’t a “lossless” for-mat Since these classes contain functions, there’s no way they can be converted to JSON and come back wholly intact But this is the minimum amount of information we need to restore the class as it existed originally JSON can’t do this automatically, but we can do it manually without much effort
Using Object Methods
Prototype defines quite a few methods on Object, the generic constructor and patriarch
of the JavaScript family Unlike the Stringmethods just covered, these aren’t instance methods—they’re attached to Objectitself
Type Sniffing with Object.isX
The hasty conception and standardization of JavaScript in the 1990s left a few gaping holes in the language One of the biggest holes is the typeofoperator; in theory it gives
us useful information about an item, but in practice it acts like Captain Obvious
Sure, it handles the simple cases just fine:
typeof "syzygy"; //-> 'string'
typeof 37; //-> 'number'
typeof false; //-> 'boolean'
Trang 2But when it’s asked to do more complex checks, it falls flat on its face:
typeof [1, 2, 98]; //-> 'object'
typeof new Date(); //-> 'object'
typeof new Error('OMG'); //-> 'object'
Arrays, Dates, and Errors are objects, of course, but so are all non-primitives Imagine
if you asked me, “Do you know what time it is?” and I responded only with “Yes.” My
answer is both narrowly correct and completely useless
The one other value returned by typeofis function, but that applies both to functions
and regular expressions:
typeof $; //-> 'function'
typeof /bẳ:r|z)/; //-> 'function'
In other words,typeofarbitrarily singles out functions, even though they’re
instances of Objectjust like Array,Date, and Errortypes And the fact that RegExpis a
function (if we’re being technical about it) just makes it harder to distinguish them
from true functions—what a useless taxonomỵ
Prototype includes the type checking that the language left out In a dynamic,
browser-based scripting environment, some checks happen again and again Prototype
defines a handful of methods that test for certain data types: they all accept one
argu-ment and return either trueor falsẹ
The Object.isString, Object.isNumber, Object.isFunction Methods
The three simplest of these functions behave exactly like their typeofequivalents:
Object.isString("foo"); //-> true
Object.isNumber("foo"); //-> false
Object.isFunction($); //-> true;
Object.isNumber(4); //-> true
Object.isNumber(3 + 9); //-> true
Object.isNumber("3" + 9); //-> false
Skeptics may wonder why these methods are defined at all—if the behavior is
identi-cal to typeof, why not use typeofinstead? These methods are slightly shorter to type than
the alternative, but they exist mostly so that you’ll get out of the habit of using typeof If
that bothers you, feel free to ignore them for philosophical reasons
Trang 3The Object.isUndefined Method
Here’s another nugget of JavaScript trivia, in case you didn’t know: nulland undefinedare two separate values If you asked to borrow a dollar, nullwould be a firm, loud reply of
“No!”; undefinedwould be a distant stare, as if I hadn’t heard your question
In other words, undefinedmeans that a value has not been set for a variable, and null
means that the value has been explicitly set to nothing For example, if I omit a named
argument from a function, it will be set to undefined:
function amigos(first, second, third) {
alert(typeof third);
}
amigos("Lucky", "Ned", "Dusty"); // alerts "string"
amigos("Lucky", "Ned"); // alerts "undefined"
JavaScript treats these two properties differently just to screw with our heads: typeof undefined; // "undefined"
typeof null; // "object"
Huh? Why is nullan object? Do you mean for us to scream?
Fortunately, you can test for nullby using a strict equivalence check (using three equals signs, rather than two) For example, consider Element#readAttribute, which returns nullif the specified attribute does not exist:
if ($('lastname').readAttribute('required') === null)
alert("Last name not required.");
On the other hand, the canonical way to check for undefinedis to use typeof We cannot allow that, however Instead, we’ll use Object.isUndefined:
function amigos(first, second, third) {
alert(Object.isUndefined(third));
}
The Object.isArray, Object.isHash, Object.isElement Methods
The three remaining type-checking methods test for arrays, hashes (instances of Proto-type’s Hashclass), and DOM element nodes Each of these would respond to typeofwith
“object,” but this is unacceptable for arrays and DOM nodes in particular, since they’re among the most commonly used objects in the browser environment
Trang 4var amigos = ["Lucky", "Ned", "Dusty"];
typeof amigos; //-> 'object'
Object.isArray(amigos); //-> true
var villain = $('el_guapo');
typeof villain; //-> 'object'
Object.isElement(villain); //-> true
var partyFavors = $H({
cake: 1,
amigos: 3,
pinatas: "plethora"
});
typeof partyFavors; //-> 'object'
Object.isHash(partyFavors); //-> true
Using Type-Checking Methods in Your Own Functions
JavaScript’s weak typing is a boon to API developers, since it allows for functions that
can take many different combinations of arguments Consider Prototype’s own $: it can
accept any number of arguments, each of which can be either a string or an element.
Let’s look at the source code for $:
function $(element) {
if (arguments.length > 1) {
for (var i = 0, elements = [], length = arguments.length; i < length; i++)
elements.push($(arguments[i]));
return elements;
}
if (Object.isString(element))
element = document.getElementById(element);
return Element.extend(element);
}
The first ifstatement handles the case where there is more than one argument: $
loops through the arguments, cleverly calls itself on each one, and then places each result
into an array, returning that array
The final three lines deal with the common case: elementis either a DOM element
node or a string, so the function sniffs for strings and calls document.getElementByIdto
convert them into elements Thus, by the last line, elementis guaranteed to be a true
DOM node
Trang 5Less dynamic languages like Java would require that we define $twice: once for pass-ing it a strpass-ing and once for passpass-ing it an element (Passpass-ing an indeterminate number of arguments wouldn’t work at all.) JavaScript, on the other hand, lets us write one function for all these use cases; we have the tools to inspect these arguments ourselves
In the case of $, performing a simple type check within the function saves the devel-oper’s time: she can write functions that accept strings and DOM nodes indifferently Calling $within those functions will ensure that the end result is an element
Think how much time you could save by automating the annoying type coercions that your functions demand? Prototype’s type-checking methods allow your code to read your mind just a bit better
Using Array Methods
Way back in Chapter 3, I covered Enumerableand the methods it adds to collections Arrays receive these methods, of course, but they also receive a handful of methods tailored specifically for arrays
The reverse and clear Methods
The first two methods document themselves Array#reverseinverts the order of an array; Array#clearremoves all of an array’s items
Array#reversereturns a new array by default, but takes an optional Boolean argu-ment to reverse the original array:
var presidents = ["Washington", "Adams", "Jefferson", "Madison", "Monroe"];
presidents.reverse();
//-> ["Monroe", "Madison", "Jefferson", "Adams", "Washington"]
presidents;
//-> ["Washington", "Adams", "Jefferson", "Madison", "Monroe"];
presidents.reverse(true);
//-> ["Monroe", "Madison", "Jefferson", "Adams", "Washington"]
presidents;
//-> ["Monroe", "Madison", "Jefferson", "Adams", "Washington"]
Trang 6For obvious reasons, Array#clearalways acts on the original array:
presidents.clear();
presidents; //-> []
The uniq and without Methods
Two other methods winnow the contents of the array Array#uniqtakes its behavior and
its odd name from the corresponding Ruby method—it returns an array without any
duplicate values:
var presidents = ["Washington", "Adams", "Jefferson",
"Madison", "Monroe", "Adams"];
presidents.uniq();
//-> ["Monroe", "Madison", "Jefferson", "Adams", "Washington"]
[1, 2, 3, 3, 2, 3, 1, 3, 2].uniq();
//-> [1, 2, 3]
Array#withoutaccepts any number of arguments and returns a new array without the
specified values:
var presidents = ["Washington", "Adams", "Jefferson",
"Madison", "Monroe", "Adams"];
presidents.without("Adams", "Monroe");
//-> ["Washington", "Jefferson", "Madison"];
Summary
Now that I look back on it, I realize that this chapter hasn’t been too embarrassing for
either of us I managed to fill in a few of the cracks I had skipped over in previous
chap-ters, and you got a broader look at the some of the APIs Prototype provides
Part 2 of this book will focus on script.aculo.us and its robust effects library and
set of UI controls We’ll revisit everything covered in this chapter as we explore
script.aculo.us through hands-on examples