Our toStringmethod returned the first and last names of the player: var qb = new Quarterback"Andrew", "Dupont"; qb; //-> [Object]; qb + " just won the Heisman trophy."; //-> "Andrew Dupo
Trang 1Advanced Replacement
Recall in the previous chapter how we created classes for football players, where each instance of a class was a specific player Each had properties like firstName,lastName, and
We also defined a custom toStringmethod, taking advantage of the fact that
JavaScript uses a method by that name whenever it needs to coerce an object into a string Our toStringmethod returned the first and last names of the player:
var qb = new Quarterback("Andrew", "Dupont");
qb; //-> [Object];
qb + " just won the Heisman trophy.";
//-> "Andrew Dupont just won the Heisman trophy."
Because concatenating (+) a string to another object involves automatic string coercion, our instance of Quarterbackknew how to represent itself in that context
inter-polating with has a special method called toTemplateReplacements, then the result of that method will be used to fill in the template string:
var blatantLie = new Template(
"#{position} #{firstName} #{lastName} just won the Heisman trophy.");
blatantLie.evaluate(qb);
//-> " Andrew Dupont just won the Heisman trophy."
// adding an instance method to the Quarterback class
Class.extend(Quarterback, {
toTemplateReplacements: function() {
return {
position: "QB",
firstName: this.firstName,
lastName: this.lastName
};
}
});
blatantLie.evaluate(qb);
//-> "QB Andrew Dupont just won the Heisman trophy."
Here, we’ve done a before-and-after comparison The first time we call
Trang 2in the holes of the template string That instance has firstNameand lastNameproperties,
but it doesn’t have a positionproperty, so an empty string is used instead
When we add Quarterback#toTemplateReplacements, though, we’re supplying a
substi-tute object to use for interpolation So we’re able to specify properties above and beyond
those that already exist on the object In this case, we’re defining a positionproperty to
go into the evaluated string
The larger purpose of defining toTemplateReplacementsis the same as the purpose
as defining toString: it allows you to specify in one place how your object will behave in
a given context For user-defined classes, it also promotes encapsulation, one of those
hallowed OOP virtues
It’s all part of the theme that was established in Chapter 3: ensuring that objects
come with instructions for use Just like we rely on objects that mix in Enumerableto know
how to enumerate themselves, here we’re relying on objects to know how to represent
themselves in a Templatecontext
Bringing It Back to String#gsub
The versatility of JavaScript objects gives us a bonus way to use Template Remember that
an array is really just an object with numerals as keys, so Templateand String#interpolate
can be used with arrays as well:
var sample = new Template("The quick brown #{0} jumps over the lazy #{1}");
sample.evaluate(["fox", "dog"]);
//-> "The quick brown fox jumps over the lazy dog."
Items of an array respond to property lookups just like any other object
Finally, then, I can share an Easter egg of sorts—template strings like these can be
used in String#gsub:
var warning = "Never, never pour salt in your eyes."
warning.gsub(/(salt) in your (eyes)/, "#{2} in your #{1}");
//-> "Never, never pour eyes in your salt."
You may also recall that the first item in a match array is the entire string that
matches the pattern; any subsequent items are substrings that match specific captures in
the regular expression The preceding example, then, is shorthand for either of these:
Trang 3warning.gsub(/(salt) in your (eyes)/, function(match) {
return match[2] + " in your " + match[1];
});
// or
warning.gsub(/(salt) in your (eyes)/, function(match) {
return "#{2} in your #{1}".interpolate(match);
});
OK, I take it back: maybe strings are more awesome than we realized.
Using JSON
Strings are the building blocks of high-level protocols like HTTP A client and a server communicate in plain text, which, while much less friendly than the rendered view of a browser, is nonetheless human-readable
Each time your browser requests an HTML file, it receives one gigantic string, which
it then converts into a tree of objects for rendering It converts between the two
accord-ing to the rules of HTML It can be said, then, that HTML is a serializer: it takes somethaccord-ing
inherently nonlinear and makes it linear for storage purposes
The interesting stuff is done at higher levels, with more complex data types But on
the Web, sooner or later, it all ends up as a string JSON (JavaScript Object Notation) is
simply a way to represent these complex structures as strings
What Does JSON Look Like?
Prototype likes to leverage the literal syntax for object creation, so code like this should
be nothing new to you:
var vitals = {
name: "Andrew Dupont",
cities: ["Austin", "New Orleans"],
age: 25
};
We can describe data structures in JavaScript with a minimum of syntactic cruft By comparison, let’s see what this data would look like in XML:
Trang 4<name>Andrew Dupont</name>
<cities>
<city>Austin</city>
<city>New Orleans</city>
</city>
<age>25</age>
</vitals>
XML is verbose by design What if we don’t need its extra features? That’s where JSON
comes in
Why JSON?
There are a million different ways to exchange data between client and server; what’s so
special about JSON?
Very little, really It’s not magical; it’s simply the right tool for the job in
JavaScript-heavy web apps There are JSON libraries for all the common server-side languages: PHP,
Ruby, Python, Java, and many others It’s far simpler than XML, and thus far more useful
when you don’t need all of XML’s bells and whistles
We’ll revisit JSON in Part 2 of this book when we look into advanced topics in Ajax
But let’s familiarize ourselves with the basics right now
Serializing with Object.toJSON
The best way to learn about the structure of JSON is to try it out yourself Let’s create a
few different JavaScript data types and see how they look in JSON:
var str = "The Gettysburg Address";
var num = 1863;
var arr = ["dedicate", "consecrate", "hallow"];
var obj = {
name: "Abraham Lincoln",
location: "Gettysburg, PA",
length: 269
};
Trang 5Object.toJSON(str); //-> '"The Gettysburg Address"'
Object.toJSON(num); //-> '1863
Object.toJSON(arr);
//-> '["dedicate", "consecrate", "hallow"]'
Object.toJSON(obj);
//> '{ "name": "Abraham Lincoln", "location": "Gettysburg, PA",
"length": 269 }'
These examples teach you several new things:
• The way items are represented in JSON is identical to how they’re represented in
JavaScript JSON conforms to the syntax and grammar of JavaScript
In other words, in each of these cases, the string representation of the data matches
the keyboard characters you’d type to describe them yourself.
JSON can serialize any JavaScript data type except for functions and regular expres-sions The Datetype gets half credit: there’s no literal notation for dates, so they’re
converted to a string representation
Unserializing with String#evalJSON
The ease of dealing with JSON in JavaScript should be obvious: since it’s valid code, it can simply be evaluated JavaScript includes an evalfunction that will evaluate a string as though it were code:
var data = eval('{ "name": "Abraham Lincoln", "location": "Gettysburg, PA",
"length": 269 }');
//-> [Object]
But there’s a problem It’s always dangerous to evaluate arbitrary code unless you know exactly where it’s coming from Prototype’s String#evalJSONwill evaluate a string as code, but it takes an optional Boolean; if true, it will make sure the given string is valid JSON—and therefore not a security risk
Trang 6var str = '{ "name": "Abraham Lincoln", "location": "Gettysburg, PA",
"length": 269 }';
str.evalJSON(); //-> [Object]
// Ensuring it's valid JSON
str.evalJSON(true); //-> [Object]
// Now try it with invalid, malicious JSON
str = '{ "name": "Abraham Lincoln" }; doSomethingMalicious();'.evalJSON(true);
//-> SyntaxError: Badly formatted JSON string
Most of the time, you’ll be using JSON simply as a way to communicate with your
own server, so security won’t be an issue But if you happen to be handling JSON from a
third party, you must make sure it’s safe to use.
Overriding the Default Serialization
reporting some properties and ignoring all others:
// Just first name, last name, and points
Class.extend(Player, {
toJSON: function() {
return Object.toJSON({
firstName: this.firstName,
lastName: this.lastName
});
}
});
// But maybe subclasses should also report their position
Class.extend(Quarterback, {
toJSON: function() {
return Object.toJSON({
position: 'QB',
firstName: this.firstName,
lastName: this.lastName,