JavaScript
Content
Intro
Grammar
Objects
Functions
Inheritance
Arrays
Intro
Intro
- Language of the browser
- Thus - one of the most popular programming languages of the world
- At the same time, there is a lot of controversy around JS
- DOM
- Awful API, JS is blamed
- Poorly specified, inconsitent implementation
- JS
- Expressive language
- Get things done without knowing all parts
Intro #2
- JS based on some good and bad ideas
- Good ideas
- Functions, loose typing, dynamic objects, expressive object literal notation
- Bad ideas
- Programming model based on global variables
- Functions are first class objects
- First main stream lambda language
- JS more in common with Lisp and Scheme than Java
- JS ~ Lisp in C clothing
Intro #3
- Most programming languages demand strong typing
- Detect large class of errors at compile time
- JS loosely typed language
- Unable to detect type errors
- Strong vs. loosely typing
- Strong typing still needs careful testing
- Loose typing liberating
- No complex class hierarchies
- No fighting with the type system
- Powerful object literal notation (inspired JSON)
- Prototypal inheritance
- Controversal feature
Grammar
Whitespace
- Used to format characters or comments
- Usually insignificant
- Occassionally used to separate sequences of characters that would be combined into a single token
- var myVar = 'Hi';
- Space between var and myVar cannot be removed but other whitespace is optional
- var myVar='Hi';
- Comments
- Block comments: /* */
- Line-ending comments: //
- Use Line-ending comments
- /* may occur in regular expressions
- Write accurate comments, bad comments are worse than no comments
Names
- Name is a letter optionally followed by one or more letters, digits, or underbars
- Name cannot be a reserved word
- abstract
- boolean break byte
- case catch char class const continue ...
- Some words that cannot be used and are not reserved
- undefined NaN Infinity
- Names are used for statements, variables, parameters, property names, operators, and labels
- Not permitted to name a variable or paramter with a reserved word
- Not permitted as object property
Numbers
- JS has single number type
- Internally represented as 64-bit floating point
- double in Java
- 1 and 1.0 are the same
- All you have to know about a number is that it's a number
- Large class of numeric type errors are avoided
- Numbers may have exponent part
- 100 ~ 1e2
- Negative numbers by - prefix operator
- NaN is a number value of result of an operation that cannot produce a normal result
- NaN is not equal to any value except to itself
- isNaN(number) may be used to detect NaN
- Infinity all numbers greater 1.79769313486231570e+308
Strings
- String literal may be packed in "abc" or 'abc'
- Zero or more characters
- \ (backslash) is escape character
- JS characters are 16-bit wide
- At beginning of JS Unicode was 16-bit character set
- JS does not have a character type
- Escape sequence allow for inserting characters into strings that are not normally permitted
- 'A' === '\u0041'
- String have a length property
- 'JavaScript'.length is 10
- Strings are immutable
- Concatenating strings with + operator
- 'c' + 'a' + 't' === 'cat'
Statements
- Compilation unit contains set of executable statements
- Each <script> tag delivers a compilation unit, that is compiled and executed
- JS has no linker
- throws all compilation units in a global namespace
- var statements
- var myVar = 'test';
- When used in functions, var statement defines function's local variables
- switch, while, for, and do statements are allowed to have a label statement as prefix (interact with break statement)
Statements #2
- Statements tend to be executed in order
- from top to bottom
- Alter sequence of execution with conditional statements
- if and switch
- Looping statements: while, for, do
- Disruptive statements: break, return, throw
- function invocation
- Block is a set of statements wrapped in curly braces
- Blocks in JavaScript do NOT create a new scope
- Then block of if statement is executed if the expression is truthy
- Falsy values: false, null, undefined, '', 0, NaN
- All other values are truthy:
- true, 'false', objects, ...
Expressions
- literal values (strings, numbers)
- variables
- built-in values (true, false, null, undefined, ...)
- invocation expression preceded by new
- refinement expression preceded by delete
- expression wrapped in parantheses
- expression preceded by a prefix operator
- expression followed by
- Infix operator or another expression
- Ternary operator (?:)
- Invocation
- Refinement
Operator Precedence
. [] ( ) Refinement and invocation
delete new typeof + - ! Unary operators
* / % Multiplication, division, modulo
+ - Addition/concatenation, subtraction
>= <= > < Inequality
=== !== Equality
&& Logical and
|| Logical or
?: Ternary
Objects
JavaScript Types
- Simple types are numbers, strings, booleans (true, false), null and undefined
- All other values are objects
- Numbers, strings and booleans are object like
- they have methods
- but immutable
- Objects in JavaScript are mutable keyed collections
- Arrays, functions and regular expressions are objects too
JavaScript Types #2
- Object is a container of properties
- Each property has a name and a value
- Property name can be any string (including empty string)
- Property value can be any value except for undefined
- Objects in JavaScript are class-free
- No constraints on the names or values of properties
- Objects are useful for collecting and organizing data
- Objects can contain other objects
- JavaScript provides prototype feature
- Allows an object to inherit the properties of another
- Reduces object init time and memory consumptions
Object Literals
var empty_object = {};
var stooge = {
"first-name": "Jerome",
"last-name": "Howard"
};
var flight = {
airline: "Oceanic",
number: 815,
departure: {
IATA: "SYD",
time: "2004-09-22 14:55",
city: "Sydney"
},
arrival: {
IATA: "LAX",
time: "2004-09-23 10:42",
city: "Los Angeles"
}
};
Object Retrieval
stooge["first-name"] // "Joe"
stooge["middle-name"] // undefined
flight.status // undefined
stooge["FIRST-NAME"] // undefined
var middle = stooge["middle-name"] || "(none)";
var status = flight.status || "unknown";
flight.equipment // undefined
flight.equipment.model // throw "TypeError"
flight.equipment && flight.equipment.model // undefined
Object Update
stooge['first-name'] = 'Jerome';
// If the object does not already have that property name, the object is augmented:
stooge['middle-name'] = 'Lester';
stooge.nickname = 'Curly';
flight.equipment = {
model: 'Boeing 777'
};
flight.status = 'overdue';
Object Reference
var x = stooge;
x.nickname = 'Curly';
var nick = stooge.nickname;
// nick is 'Curly' because x and stooge
// are references to the same object
var a = {}, b = {}, c = {};
// a, b, and c each refer to a
// different empty object
a = b = c = {};
// a, b, and c all refer to
// the same empty object
Prototype
- Every object is linked to a prototype object from which it can inherit properties
- All objects created from object literals are linked to Object.prototype
- Std object in JavaScript
- When creating a new object, you may select the prototype
Prototype #2
if (typeof Object.create !== 'function') {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
}
var another_stooge = Object.create(stooge);
another_stooge['first-name'] = 'Harry';
another_stooge['middle-name'] = 'Moses';
another_stooge.nickname = 'Moe';
- When object is updated, the prototype is not touched
- Prototype link has just effect on updating
- If we try to retrieve property from an object, object lacks that property
- Runtime tries to retrieve it from the prototype object
- All up the prototype chain (delegation)
- If property does not exist in prototype chain - undefined
Prototype #3
stooge.profession = 'actor';
another_stooge.profession // 'actor'
- The prototype relationship is dynamic
- If we add a new property to a prototype, that property will immediately be visible in all of the objects that are based on that prototype
Reflection
typeof flight.number // 'number'
typeof flight.status // 'string'
typeof flight.arrival // 'object'
typeof flight.manifest // 'undefined'
typeof flight.toString // 'function'
typeof flight.constructor // 'function'
// typeof consideres the whole prototype chain
// hasOwnProperty checks own object
flight.hasOwnProperty('number') // true
flight.hasOwnProperty('constructor') // false
Delete
another_stooge.nickname // 'Moe'
// Remove nickname from another_stooge, revealing
// the nickname of the prototype.
delete another_stooge.nickname;
another_stooge.nickname // 'Curly'
- Delete operator may used to remove a property from an object
- It will remove a property from the object if it has one
- Will not touch objects in the prototype linkage
- Removing a property from an object may reveal prototype property
Functions
Function Objects
- Best thing about JavaScript is the implementation of functions
- Functions enclose a set of statements
- Fundamental modular unit of JavaScript
- Used for code reuse, information hiding and composition
- Functions in JavaScript are objects
- Objects are a collection of name/value pairs having a hidden link to a prototype object
Function Objects #2
- Object produced from object literal are linked to Object.prototype
- Function objects are linked to Function.prototype which itself is linked to Object.prototype
- Since functions are objects they can be used like any other value
- Can be stored in variables, objects and arrays
- Functions can be passed as function argument, and functions may return functions
- Functions can be invoked
Function Literal
// Create a variable called add and store a function
// in it that adds two numbers.
var add = function (a, b) {
return a + b;
};
- Function literal has 4 parts
- function keyword
- function name
- anonymous if not given
- function parameters wrapped in parantheses
- function body
- set of statements wrapped in curly braces
Closure
- function literal can appear everywhere where an expression is valid
- functions can be defind inside functions
- Inner function has access to parameters and variables of outer function
- function object created by a function literal contains a link to the outer context (closure)
- Powerful language property
Method Invocation
- Suspends the execution of the current function, passing control and parameters to the new function
- In addition to declared parameters every function receives two additional parameters: this and arguments
- Value of this parameters determined by invocation pattern
- Invocation operator is a pair of parantheses that follows any expression that produces a function value
- Parantheses can contain zero or more expressions that produces a parameter value
- No runtime error when number of arguments and number of parameters do not match
- If there are too few arguments, missing values will be undefined
- If there are too much arguments, they will be ignored
Method Invocation Pattern
// Create myObject. It has a value and an increment
// method. The increment method takes an optional
// parameter. If the argument is not a number, then 1
// is used as the default.
var myObject = {
value: 0,
increment: function (inc) {
this.value += typeof inc === 'number' ? inc : 1;
}
};
myObject.increment( );
document.writeln(myObject.value); // 1
myObject.increment(2);
document.writeln(myObject.value); // 3
- When a function is stored as a property of an object, we call it a method
- When a method is invoked, this argument is bound to that object
- this parameter can be used to access properties of the object
Function Invocation Pattern
var sum = add(3, 4);
// Augment myObject with a double method.
myObject.double = function() {
var that = this; // Workaround.
var helper = function() {
that.value = add(that.value, that.value);
};
helper(); // Invoke helper as a function.
};
// Invoke double as a method.
myObject.double();
document.writeln(myObject.getValue( )); // 6
- When a function is not a property of an object, then it is invoked as a function
- this parameter is bound to the global object! (design mistake)
- Easy workaround
Constructor Invocation Pattern
- JavaScript is a prototypal inheritance language
- Objects can inherit properties directly from other objects
- No classes!
- Differnt to most of the current languages
- Therefore prototypal nature is hidden with strange object-making syntax
- If function is invoked with the new prefix, then a new object will be created with a hidden link to the value of the function's prototype member
Constructor Invocation Pattern #2
// Create a constructor function called Quo.
// It makes an object with a status property.
var Quo = function (string) {
this.status = string;
};
// Give all instances of Quo a public method
// called get_status.
Quo.prototype.get_status = function ( ) {
return this.status;
};
// Make an instance of Quo.
var myQuo = new Quo("confused");
document.writeln(myQuo.get_status( )); // confused
- Functions that are intended to be used with the new prefix are called constructors
- Variable names should be capitalized names
The Apply Invocation Pattern
// Make an array of 2 numbers and add them.
var array = [3, 4];
var sum = add.apply(null, array); // sum is 7
// Make an object with a status member.
var statusObject = {
status: 'A-OK'
};
// statusObject does not inherit from Quo.prototype,
// but we can invoke the get_status method on
// statusObject even though statusObject does not have
// a get_status method.
var status = Quo.prototype.get_status.apply(statusObject);
// status is 'A-OK'
- Apply method lets us construct an array of arguments to use to invoke a function
- Also allows to choose the value of the this argument
- First argument is the this value
- Second argument is an array of parameters
Arguments Parameter
var sum = function ( ) {
var i, sum = 0;
for (i = 0; i < arguments.length; i += 1) {
sum += arguments[i];
}
return sum;
};
document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108
- Bonus parameter that is available to functions when they are invoked
- Gives access to all function parameters including excess arguments
- Makes it possible to write functions that take an unspecified number of parameters
Return
- When a function is invoked it begins execution with the first statement and ends on }
- Return statements can be used to return earlier
- Function always return a value, if not specified it will be unspecified
- If the function was invoked with the new prefix and the return value is not an object it will return the value of this
Exceptions
var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name: 'TypeError',
message: 'add needs numbers'
};
}
return a + b;
}
- Throw statement interrupts execution of the function
- Exception object should contain a name and message property
Exceptions #2
// Make a try_it function that calls the new add function incorrectly.
var try_it = function () {
try {
add("seven");
} catch (e) {
document.writeln(e.name + ': ' + e.message);
}
}
try_it();
- Exception object is delivered to the catch clause of a try statement
- Try statement has a single catch block that will catch all exceptions
- Inspect name property to determine type of exception
Augmenting Types
Function.prototype.method = function (name, func) {
this.prototype[name] = func;
return this;
};
- JavaScript allows the basic types of the language to be augmented
- When adding a method to Object.prototype it is available to all objects
- Same works for functions, arrays, strings, numbers, regular expressions
- Example: make a function available to all functions
Recursion
var hanoi = function (disc, src, aux, dst) {
if (disc > 0) {
hanoi(disc - 1, src, dst, aux);
document.writeln('Move disc ' + disc +
' from ' + src + ' to ' + dst);
hanoi(disc - 1, aux, src, dst);
}
};
hanoi(3, 'Src', 'Aux', 'Dst');
- Recursive function calls itself
- Towers of Hanoi
- Tail recursion optimization
- If a function returns the result of invoking itself, recursion is replaced by a loop
- Speed up
Scope
var foo = function () {
var a = 3, b = 5;
var bar = function () {
var b = 7, c = 11;
// At this point, a is 3, b is 7, and c is 11
a += b + c;
// At this point, a is 21, b is 7, and c is 11
};
// At this point, a is 3, b is 5, and c is not defined bar();
// At this point, a is 21, b is 5
};
- Scope controls visibility and lifetimes of variables and parameters
- Important for developers
- Reduces naming collisions
- Provides automatic memory management
Scope #2
- Most languages with C syntax have block scope
- All variables in a block are not visible from outside of the block
- Variables may be released when the execution of the block is finished
- JavaScript does not have block scope
- Function scope
- Variables defined in a function are not visible outside of the function
- Declare all variables at the top of a function
Closure
var myObject = function () {
var value = 0;
return {
increment: function (inc) {
value += typeof inc === 'number' ? inc : 1;
},
getValue: function () {
return value;
}
};
}();
- Inner functions get access to parameters and variables of the functions they are defined within (expect this and arguments)
Callbacks
// sync - client is blocked
request = prepare_the_request();
response = send_request_synchronously(request);
display(response);
// async
request = prepare_the_request();
send_request_asynchronously(request, function (response) {
display(response);
});
Cascade
getElement('myBoxDiv').
move(350, 150).
width(100).
height(100).
color('red').
border('10px outset').
padding('4px').
appendText("Please stand by").
on('mousedown', function (m) {
this.startDrag(m, this.getNinth(m));
}).
on('mousemove', 'drag').
on('mouseup', 'stopDrag').
later(2000, function () {
this.color('yellow').
setHTML("What hath God wraught?").
slide(400, 40, 200, 200);
}).
tip('This box is resizeable');
- Some methods do not have return value
- If we return this instead of undefined, cascade is enabled
Inheritance
Intro
- Promotes code reuse
- Introduces a type system
- In classical languages
- Objects are instances of classes
- Class can inherit from another class
- JavaScript prototypal style
- Objects inherit directly from other objects
- Many code reuse patterns
Pseudoclassical
var Mammal = function (name) {
this.name = name;
};
Mammal.prototype.get_name = function () {
return this.name;
};
Mammal.prototype.says = function () {
return this.saying || '';
};
var myMammal = new Mammal('Herb the Mammal');
var name = myMammal.get_name(); // 'Herb the Mammal'
- JavaScript hides prototypal nature
- Instead of objects inheriting directly from objects and unnecessary level of indirection is used
- Objects are produced by constructors
Pseudoclassical #2
var Cat = function (name) {
this.name = name;
this.saying = 'meow';
};
// Replace Cat.prototype with a new instance of Mammal
Cat.prototype = new Mammal();
// Augment the new prototype with
// purr and get_name methods.
Cat.prototype.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
Cat.prototype.get_name = function () {
return this.says() + ' ' + this.name + ' ' + this.says();
};
var myCat = new Cat('Henrietta');
var says = myCat.says(); // 'meow'
var purr = myCat.purr(5); // 'r-r-r-r-r'
var name = myCat.get_name(); // 'meow Henrietta meow'
Pseudoclassical #3
- All properties are public!
- Constructor has to be called with new keyword
- Provides comfort to programmers who are unfamiliar with JavaScript
Object Specifiers
var myObject = maker(f, l, m, c, s);
var myObject = maker({
first: f,
last: l,
state: s,
city: c
});
- Constructor with large number of parameters
- Can be difficult to remeber the order of the arguments
- Rewrite the constructor to accept a single object
- Arguments can be listed in any order
Prototypal
var myMammal = {
name : 'Herb the Mammal', get_name : function () {
return this.name;
},
says : function () {
return this.saying || '';
}
};
var myCat = Object.create(myMammal);
myCat.name = 'Henrietta';
myCat.saying = 'meow';
myCat.purr = function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
};
myCat.get_name = function () {
return this.says() + ' ' + this.name + ' ' + this.says();
};
Prototypal #2
- Prototypal inheritance is conceptually simpler than classical inheritance
- New object can inherit the properties of an old object
- Make a useful object
- Construct similar objects via differential inheritance
- Customizing a new object, specify the difference from the object on which it is based on
Functional
var mammal = function (spec) {
var that = {};
that.get_name = function () {
return spec.name;
};
that.says = function () {
return spec.saying || '';
};
return that;
};
var myMammal = mammal({name: 'Herb'});
- With this inheritance patterns until now, we do not get privacy
- All properties visible, no private variables, no private methods
Arrays
Intro
- Linear allocation of memory
- Integers are used to compute offsets
- JavaScript does not have such an array
- JavaScript provides an object that has some array-like characteristics
- Converts array subscripts into strings that are used to make properties
- Slower than real array
- But more convenient to use
Array Literals
var empty = [];
var numbers = [
'zero', 'one', 'two', 'three', 'four',
'five', 'six', 'seven', 'eight', 'nine'
];
empty[1] // undefined
numbers[1] // 'one'
empty.length // 0
numbers.length // 10
- Provide convenient notation for creating new array values
- Pair of square brackets surrounding zero or more values separated by commas
- Array literal can appear everywhere where an expression may appear
- First value gets the property name '0'
- Second value gets the property name '1'
Array Literals #2
var numbers_object = {
'0': 'zero', '1': 'one', '2': 'two',
'3': 'three', '4': 'four', '5': 'five',
'6': 'six', '7': 'seven', '8': 'eight',
'9': 'nine'
};
- numbers array and numbers_object object produce similar results
- 10 properties, containing same values
- numbers inherits from Array.prototype
- number_object inherits from Object.prototype
- numbers inherits a larger set of useful methods
- numbers gets the length property, numbers_object does not
Array Literals #2
var misc = [
'string', 98.6, true, false, null, undefined,
['nested', 'array'], {object: true}, NaN,
Infinity
];
misc.length // 10
- In most languages all array elements must be the same type
- In JavaScript an array may contain mixture of values
Length Property
var myArray = [];
myArray.length // 0
myArray[1000000] = true;
myArray.length // 1000001
- Not upper bound for array size
- If element is stored at a subscript greater than the current length, the length is increased
- No array bound errors
- Length property is the largest integer property name +1
- Not necessarily the number of properties in the array
- Length can be explicitly set
- making it larger does not allocate more space
- making it smaller deletes all elements with subscripts greater than the length
Length Property
numbers.length = 3;
// numbers is ['zero', 'one', 'two']
numbers[numbers.length] = 'shi';
// numbers is ['zero', 'one', 'two', 'shi']
numbers.push('go');
// numbers is ['zero', 'one', 'two', 'shi', 'go']
- Use length to add new element at the end
- Or use the push method
Delete
// numbers is ['zero', 'one', 'two', 'shi', 'go']
delete numbers[2];
// numbers is ['zero', 'one', undefined, 'shi', 'go']
numbers.splice(2, 1);
// numbers is ['zero', 'one', 'shi', 'go']
- Since JavaScript arrays are objects, delete operator can be used to remove elements from an array
- Leaves a hole in the array, since elements to the left keep the names
- Use splice method
- First argument sets position
- Second argument determines num of elements to delete
- Splice takes time for big arrays
Enumeration
var i;
for (i = 0; i < myArray.length; i += 1) {
document.writeln(myArray[i]);
}
- Since JavaScript arrays are objects, for in statement may be used to iterate over all of the properties of an array
- For in makes no guarantee about the order of the properties
- Also problems with unexpected properties from prototype chain
- Conventional for statement avoids these problems
Arrays vs. Objects
- Common mistake in JavaScript is to use an object when an array is required or vice versa
- Rule: When the property names are small sequentiel integers, use an array, otherwise use an object
- JavaScript is confused about difference between arrays and objects
- typeof operator reports 'object' for arrays
- No good mechanism to distinguish between array and objects
Methods
Array.method('reduce', function (f, value) {
var i;
for (i = 0; i < this.length; i += 1) {
value = f(this[i], value);
}
return value;
});
- JavaScript provides a set of methods for acting on arrays
- Functions stored in Array.prototype
- Array.prototype and Obejct.prototype may be augmented
- Reduce method takes a function and a starting value
- For each element of the array, it calls the function with an element and the value, and computes a new value
- E.g. pass in function that adds integers, result will be the sum
Method #2
Array.method('reduce', function (f, value) {
var i;
for (i = 0; i < this.length; i += 1) {
value = f(this[i], value);
}
return value;
});
var data = [4, 8, 15, 16, 23, 42];
var add = function (a, b) {
return a + b;
};
var sum = data.reduce(add, 0); // sum is 108
Dimensions
Array.dim = function (dimension, initial) {
var a = [], i;
for (i = 0; i < dimension; i += 1) {
a[i] = initial;
}
return a;
};
// Make an array containing 10 zeros.
var myArray = Array.dim(10, 0);
- JavaScript arrays are not initialized
- If you ask for a new array with [], it will be empty
- If you access a missing element, undefined will be returned
- Some algorithms assume default value (0)
Dimensions #2
var matrix = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]
];
matrix[2][1] // 7
Array.matrix = function (m, n, initial) {
var a, i, j, mat = [];
for (i = 0; i < m; i += 1) {
a = [];
for (j = 0; j < n; j += 1) {
a[j] = initial;
}
mat[i] = a;
}
return mat;
};
- Array of arrays
- Write own init method
References
JavaScript: The Good Parts
Douglas Crockford
Thank you for your attention!
JavaScript
By dinony
JavaScript
JavaScript
- 444