javascript: the good parts (book report)kahl/cas706/2010/pres/js... · 2010-11-18 · prototypical...
TRANSCRIPT
JavaScript: The Good Parts (Book Report)
Michal Dobrogost
November 18th, 2010
Introducing JavaScriptHistory
I Java(tm) applets failed, JavaScript took over
I Shunted from non-existance to world wide useI Bad parts resulting from
I Poor specification =⇒ poor portabilityI Difficult to read and modify codeI Design mistakes
I “Beautiful, elegant, highly expressive language” buried inside
Introducing JavaScriptFeatures
Distinguishing features of JavaScript
I Dynamic typing
I First class functions
I Object literal notation
I Prototypal inheritance
Introducing JavaScriptHello World!
-----------------------------------------
hello.html:
-----------------------------------------
<html><body>
<script src="hello.js">
</script>
</body></html>
-----------------------------------------
hello.js:
-----------------------------------------
document.writeln(’Hello world!’);
SyntaxBasics
I Comments : C-Style // or /* */ pairsI Do not use /* */ because they can occur in the program
/*
var x = /y*/.exec("yyyyyyy");
*/
I Names : letter (letter | digit | )*
I Numbers : 23, 1.7, 1.1e-10, NaN, Infinity
I Strings : Unicode character set
"d" + ’o’ + "g" + ’\t’ === ’dog\t’
I Boolean false : false, null, undefined, ””, 0, NaN
I Boolean true : everything else
SyntaxStatements
I Variable declaration
var x = 3, y = 1, z = 4;
I If
if (x === 4) {
<statements>
} else {
<statements>
}
I Switch
switch (x + y) {
case 0:
case 0 + 1:
y = 3; // <statements>
break // <disruptive>
}
SyntaxLoops
I For, while and do while loops similar to C
for (i = 0; i < 10; i += 1) {
<statements>
}
I For-in loop
for (x in xs) {
// Ensure x not from prototype chain
if (xs.hasownProperty(x)) {
...
}
}
SyntaxExceptions, Functions
try {
throw "ERROR";
}
catch (e) {
if (e === "ERROR") {
...
}
}
function f(x,y) {
var z = x + y;
return z;
}
SyntaxExpressions
Railroad diagram from [1].
I prefix : typeof, + (to number), - (negate), ! (logical not)
I conditional : < expr > ? < expr > : < expr >
I invocation : function(< expr >,< expr >)
I refinement : record.property, array[< expr >]
SyntaxLiterals
Literals specify an object inline in the code.
I object : { name : ”bob”, ’age’ : 37 }I array : [1,1,2,3,5,8]I regexp : /[abc]*/ g i m
I g =⇒ global, match multiple timesI i =⇒ case insensitiveI m =⇒ multiline
SyntaxLiterals
Literals specify an object inline in the code.
I object : { name : ”bob”, ’age’ : 37 }I array : [1,1,2,3,5,8]I regexp : /[abc]*/ g i m
I g =⇒ global, match multiple timesI i =⇒ case insensitiveI m =⇒ multiline
ObjectsDefining, in detail
I Objects are passed by reference
I Reserved keywords are enclosed in ”” when used as propertiesof objects.
var delivery = {
"for" : "Dr. Kahl",
from : "Michal"
};
I Reserved keywords accessed by [”property”].
if (delivery.from === "Michal" &&
delivery["for"] === "Dr. Kahl")
{
...
}
ObjectsNon-existing fields
I Example
var delivery = {
"for" : "Dr. Kahl",
from : "Michal"
};
I Accessing non-existing property results in undefined .
delivery.contents // results in undefined
I Default values
delivery.contents || "none"
I Guarded retrieval (prevent throwing TypeError)
delivery.contents && delivery.contents.grade
ObjectsUpdates
I Example
var delivery = {
"for" : "Dr. Kahl",
from : "Michal"
};
I Change a property of the object
delivery.from = "Michal Dobrogost"
I Add a property to the object
delivery.contents = {
material : "presentation.tex",
grade : "A+"
}
I Removing a property from the object
delete delivery.contents.grade
ObjectsPrototypical inheritance
I All objects subclass Object.prototype by default
I Prototype selected at creation time
I To sidestep complexities, Crockford suggests
if (typeof Object.beget !== ’function’) {
Object.beget = function(o) {
var f = function () {};
f.prototype = o;
return new f();
}
}
ObjectsInheritance example
var parent = { bye : "world" };
var child = Object.beget(parent);
child.hi = "Maggie";
parent.bye = "Ashley";
var str = ’Goodbye ’ + child.bye + ’, hello ’ + child.hi;
document.writeln(str + "!");
Prints...
“Goodbye Ashley, hello Maggie!”.
ObjectsInheritance example
var parent = { bye : "world" };
var child = Object.beget(parent);
child.hi = "Maggie";
parent.bye = "Ashley";
var str = ’Goodbye ’ + child.bye + ’, hello ’ + child.hi;
document.writeln(str + "!");
Prints... “Goodbye Ashley, hello Maggie!”.
ObjectsReflection & Enumeration
for (prop in child) {
document.writeln("prop:" + prop);
document.writeln("own:" + child.hasOwnProperty(prop));
document.writeln("type:" + (typeof child[prop]));
document.writeln("val:" + child[prop]);
}
// prop:hi other types: number
// own:true string
// type:string object
// val:Maggie function
// undefined
// prop:bye
// own:false
// type:string
// val:Ashley
ObjectsNamespaces
No module or namespace facilities =⇒ fake it!
// Single global variable, our "namespace"
var NAMESP = {}
// All others are properties
NAMESP.parent = { bye : "world" };
NAMESP.child = Object.beget(NAMESP.parent);
NAMESP.child.hi = "Maggie";
NAMESP.parent.bye = "Ashley";
Functionsas Methods
I Functions that are properties of objects are methodsI All functions take an implicit ’this’ argument
I Bound at invocation time.
I For methods ’this’ binds to the containing object
var counter = {
val : 0,
inc : function () {
this.val += 1;
}
};
Functionsas Functions
I Functions that are not methods bind ’this’ to the global objectI Crockford: This is a design mistake for inner functions
I Should bind to the invoking function’s ’this’
var counter = {
val : 0,
inc : function (howmuch) {
var incOnce = function () {
this.val += 1; // Problem!
}
for (i = 0; i < howmuch; i += 1) {
incOnce();
}
}
};
Functionsas Functions (’that’ pattern)
I Solved by introducing a variable ’that’
I Inner function is closure =⇒ ’that’ is visible
var counter = {
val : 0,
inc : function (howmuch) {
var that = this; // Solved!
var incOnce = function () {
that.val += 1; // Solved!
}
for (i = 0; i < howmuch; i += 1) {
incOnce();
}
}
};
Functionsas Constructors
I Functions invoked with ’new’ serve as constructors
I Bind ’this’ to the object being created
var MakeHello = function () {
this.hello = "world";
}
var x = new MakeHello();
document.writeln("Hello " + x.hello + "!");
Functionsas Constructors (with inheritance)
I Functions subclass Function.prototypeI prototype property sets prototype for created object
I Now we can understand the beget function
if (typeof Object.beget !== ’function’) {
Object.beget = function(o) {
var f = function () {}; // returns new object
f.prototype = o;
return new f();
}
}
Functionsas invoked by apply
I Functions have apply propertyI apply is a method of Function.prototypeI Takes the object to bind as ’this’I Takes the an array of arguments
var sum = add.apply(null, [5,8]);
I Can be used to apply methods with ’this’ bound differently
var sister = {
msg : "I like blue"
show : function () { document.writeln(this.msg); }
};
var brother = { msg : "I like yellow" };
sister.show.apply(brother);
FunctionsScope, closures and currying
I Despite C-like syntax, a block does not start a new scope
I Inner functions have access to variables in scope at definition
var add = function(x) {
{
var z = x;
}
return function (y) { return z + y; }
}
var inc = add(1);
document.writeln(inc(11));
Prints... 12
Augmenting types
We can extend the functionality of a whole class of objects.
I functions
I strings
I numbers
I regular expressions
I booleans
Number.prototype.toInteger = function () {
return Math[this < 0 ? ’ceiling’ : ’floor’](this);
}
document.writeln( (-1.5).toInteger() );
Prints... -1
The Bad Parts(i) Hello world, gone wrong 1
if ([0] == false) { document.writeln(’Hello’); }
if ([0]) { document.writeln(’world’); }
Prints...
Hello
world
Because...
I Type conversion results in 0 === 0
I [0] is an object so it is not false
1Idea from http://jimbojw.com [3]
The Bad Parts(i) Hello world, gone wrong 1
if ([0] == false) { document.writeln(’Hello’); }
if ([0]) { document.writeln(’world’); }
Prints...
Hello
world
Because...
I Type conversion results in 0 === 0
I [0] is an object so it is not false
1Idea from http://jimbojw.com [3]
The Bad Parts(i) Hello world, gone wrong 1
if ([0] == false) { document.writeln(’Hello’); }
if ([0]) { document.writeln(’world’); }
Prints...
Hello
world
Because...
I Type conversion results in 0 === 0
I [0] is an object so it is not false
1Idea from http://jimbojw.com [3]
The Bad Parts(ii)
function fib(x) {
if (x <= 1) { return x; }
return
fib(x - 1) + fib(x - 2);
}
document.writeln(fib(1000));
What happens?
Prints undefinedBecause... JavaScript sees
function fib (x) {
if (x <= 0) { return 0; }
return;
fib (x - 1) + fib(x - 2);
}
The Bad Parts(ii)
function fib(x) {
if (x <= 1) { return x; }
return
fib(x - 1) + fib(x - 2);
}
document.writeln(fib(1000));
What happens? Prints undefinedBecause...
JavaScript sees
function fib (x) {
if (x <= 0) { return 0; }
return;
fib (x - 1) + fib(x - 2);
}
The Bad Parts(ii)
function fib(x) {
if (x <= 1) { return x; }
return
fib(x - 1) + fib(x - 2);
}
document.writeln(fib(1000));
What happens? Prints undefinedBecause... JavaScript sees
function fib (x) {
if (x <= 0) { return 0; }
return;
fib (x - 1) + fib(x - 2);
}
The Bad Parts(iii)
document.writeln(’4’ - 2);
document.writeln(’4’ + 2);
Prints...
2
42
Because...
I typeof ’4’ === ’string’
I So + acts as string concatenation
The Bad Parts(iii)
document.writeln(’4’ - 2);
document.writeln(’4’ + 2);
Prints...
2
42
Because...
I typeof ’4’ === ’string’
I So + acts as string concatenation
The Bad Parts(iii)
document.writeln(’4’ - 2);
document.writeln(’4’ + 2);
Prints...
2
42
Because...
I typeof ’4’ === ’string’
I So + acts as string concatenation
Douglas Crockford, JavaScript: The Good Parts. O’Reilly,2008.
ECMA International, ECMAScript Language Specification 5thedition 2009-12. http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf
JimboJW stackoverflow.com : Why does 2 == [2] inJavaScript? 2009-11-14. http://stackoverflow.com/questions/1724255/why-does-2-2-in-javascript
THANK YOU