2. Prerequisites
• Fundamentals of language
• Familiarity with syntax
• Distinction between DOM and JS
• Awareness of prototype and scope
• Basic understanding of this
3. Goals
• Progressive complexity
• Eye on performance
• Familiarity with subtleties
• Explanation of some of the internals
• Awareness of new ways of doing things
4. Roadmap
• How to better control our code’s flow
• How the code handles information
• How information is resolved
• How to practically apply advanced features
• Introduction to more advanced topics
6. Refresher Quiz
• What is an identifier?
• What are valid characters in identifiers?
• What type system does the language use?
• What are the primitives?
• What primitives are mutable?
7. Immutability
• Most primitives are immutable
• Type undefined is erroneously mutable
• Objects are mutable
• Literals are immutable
• Some array methods mutate, some don’t
8. Hoisting
• Identifier is known to scope before value
• Function names hoist after variables
• Value of variables never hoist
• Declaration of functions always hoist
• Reflect hoisting in coding style
10. Performance
• Primitives are best
• Arrays are second best
• Objects are worst
• Keep nesting to a minimum
• Indexes better alternatives to nesting
11. Example
var family = {
father : {
first : 'joe',
last : 'doe'
},
mother : {
first : 'jane',
last : 'doe'
}
};
var father = {
first : 'joe',
last : 'doe'
},
mother = {
first : 'jane',
last : 'doe'
};
13. Reminders
• Use typeof to determine type of variable
• Use instanceOf to determine class type
• Types via typeof are returned as strings
• Type of null is ‘object’
• Type of NaN is ‘number’
15. Refresher Quiz
• What is logic flow?
• What different ways can we control it?
• What is a comparison operator?
• What is the triple equal operator?
• What does every condition resolve for?
17. Effective Conditions
• Avoid unnecessary checks
• Order conditions from most to least likely
• Nest ifs to avoid redundant checks
• Optional braces for single statements
• Avoid function calls in conditions
18. call to multiply
Example
function multiply()
{
console.log('call to multiply');
return 100 * 100;
}
if (multiply() < 100)
console.log('lt 100');
else if (multiply() < 100 && multiply() === 1000)
console.log('lt 100 and equal to 1000');
else if (multiply() > 100 && multiply() < 100)
console.log('gt 100 and lt 100');
else if (multiply() > 100 || multiply() < 100)
console.log('gt 100 or lt 100');
else
console.log('how did we get here?');
call to multiply
call to multiply
call to multiply
call to multiply
call to multiply
gt 100 or lt 100
19. call to multiply
Example
function multiply()
{
console.log('call to multiply');
return 100 * 100;
}
var result = multiply();
if (result < 100)
if (result === 1000)
console.log('lt 100 and equal to 1000');
else
console.log('lt 100');
else if (result > 100)
console.log('gt 100');
else
console.log('how did we get here?');
call to multiply
gt 100
20. Creative Uses
• Use logical operators for defaults values
• Use ternary operator for assignments
• Use ifs for logic flow
• Nest ternary operators
• Replace conditions with maps
21. Example
function namePerson(firstName, lastName)
{
firstName = firstName || "Unknown",
lastName = lastName || "Doe";
console.log("Person's name is: ", firstName, lastName);
}
namePerson();
namePerson("Joe");
namePerson("", "Doeson");
namePerson("Bob", "Doeson");
Person's name is:
Person's name is:
Person's name is:
Person's name is:
Unknown Doe
Joe Doe
Unknown Doeson
Bob Doeson
22. Example
var x = (firstCondition)
? (trueFirstCondition)
? firstConditionTrueValue
: firstConditionFalseValue
: falseFirstConditionValue;
var y;
if (firstCondition)
if (trueFirstCondition)
y = firstConditionTrueValue;
else
y = firstConditionFalseValue;
else
y = falseFirstConditionValue;
24. Object Literals
• Lightweight
• No prototype of its own
• No private variables
• Non-inheritable
• Faster than an instantiated object
25. Example
var x = {
first : "one",
second : "two"
};
x.prototype.test = "Test!";
TypeError: Cannot set property 'test' of undefined
26. Example
Object.prototype.third = "three";
var x = {
first : "one",
second : "two"
},
y = {};
console.log(x);
console.log(y);
Object {first: "one", second: "two", third: "three"}
Object {third: "three"}
27. Example
Object.prototype.third = "three";
var x = {
first : "one",
second : "two"
},
y = {};
console.log(delete x.third);
console.log(x);
console.log(delete Object.prototype.third);
console.log(x);
true
Object {first: "one", second: "two", third: "three"}
true
Object {first: "one", second: "two"}
28. Instantiated Objects
• Use of new operator
• Comes with its own prototype
• Has a constructor
• Ideal for Object Oriented Programming
• Permits proper encapsulation
29. Example
function MyClass()
{
var myName = "Joe";
}
MyClass.prototype.test = "Test";
var x = new MyClass(),
y = {};
console.log(x);
console.log(y);
MyClass {test: "Test"}
Object {}
30. Exercise
function MyClass()
{
var myName = "Joe";
}
Object.prototype.test = "Test 1"
MyClass.prototype.test = "Test 2";
var x = new MyClass();
x.test = "Test 3";
console.log(x);
MyClass {test: "Test 3", test: "Test 2", test: "Test 1"}
31. Careful
• Ensure parent object always exists
• Careful not to cause looping references
• Accidental array to object coercions
• Resist extending Object prototype
• IE errors on trailing commas in literals
33. What can be done
• Dynamic keys introduced via [ ]
• Dot notation is optional
• Use of delete to remove keys
• Use of in to determine if key exists
• Enumerate keys using in
34. Example
var x = {
"name" : "Joe"
};
console.log(x.name);
console.log(x['name']);
var newKey = "arbitrary";
x[newKey] = "value";
x;
Joe
Joe
Object {name: "Joe", arbitrary: "value"}
35. Look ahead
• ES5 offers Object.create()
• Upcoming property descriptors
• Getters and setters
• Proxy API in ES6
• ES5 gives access to keys via Object.keys()
37. Refresher
• Global and functional
• No block scope
• The let keyword is not part of ES5
• Skipping var globalizes identifier
• Scope chain
38. Scope Types
• Lexical scope can be resolved lexically
• Dynamic scope doesn’t exist
• Lexical and global not mutually exclusive
• Name binding applies with call() and apply()
• Dynamics present with with() and eval()
39. Why to know
• Understand how variables are resolved
• Optimize code by reducing lookups
• Clarify understanding of closures
• Identify potential memory leaks
• Detect potential name resolution conflicts
43. Nesting
• Limit nesting to one level if possible
• Each level introduces a link in scope chain
• Deteriorates performance
• Careful of inadvertent nestings
• Avoid wherever possible
45. Declarations
• Best performance
• Always hoist name and definition
• Must be a source-element
• Converts to expression for non-sourced
• Using function statement
47. Expressions
• Usually anonymous
• Variable is a reference to function
• Named expressions useful for tracing
• Name accessible within function only
• Using function operator
48. Example
var referenceAx = function Ax()
{
console.log(Ax);
};
referenceAx();
Ax();
function Ax()
{
console.log(Ax);
}
ReferenceError: Ax is not defined
49. Arguments
• Always passed by value
• There is no by reference
• Always optional
• Always accessible via arguments
• Array-like object but not an array
51. Example
function prove(objectliteral)
{
var myaddress = objectliteral;
// let's copy the value of the argument that supposedly
// contains the address.
objectliteral.key = true;
objectliteral = null;
myaddress.key = 1;
//
//
//
//
now we should be modifying the key of the objectliteral's address
which the objectliteral variable no longer has since we wiped it.
Since we now have the address, we can reference the key of that
object.
}
var objectliteral = {key: false};
prove(objectliteral);
objectliteral.key;
1
52. Memoization
• Reduces need for redundant calculations
• Cache derived values where possible
• Maintain easy-to-access registries
• Use getters and setters for control
• Weigh use of closures versus conditions
53. Example
function resolveNumber()
{
var result = 100 * 100;
resolveNumber = function ()
{
return result;
};
return resolveNumber(result);
}
console.log(resolveNumber);
console.log(resolveNumber());
console.log(resolveNumber);
function resolveNumber()
{
var result = 100 * 100;
resolveNumber = function () { return result; };
return resolveNumber(result);
}
10000
function () { return result; }
55. Concept
• Defines an object
• Inheritable to inherited objects
• Propagates to all new and existing objects
• Has a constructor property
• Good way to extend objects
56. Prototype chain
• Resolve availability of method or property
• Similar to scope chain
• Each link traversal incurs cost
• Reduce inheritance for better performance
• Final link is null
57. Bad practice
• Do not extend native object prototypes
• Poor use of inheritance
• Looping without use of hasOwnProperty
• Carelessly extending an object
• Don’t forget to reset constructor
62. Overview
• Executed left-to-right
• Comma-separate multiple expressions
• Returns value of last expression
• Expressions always return a value
• Two types of expressions
64. When to use
• Match patterns in text
• Can be used with split()
• Also available with replace()
• Weigh performance versus complexity
• Time is of the essence
66. Example
var pattern = ".{1}s*[a-z]+";
var re = new RegExp("[0-9]" + pattern + "$", "gi");
var regex = /d{2}s*[A-Z]/gi;
console.log("33v 45d".match(regex));
console.log("4@jK".match(re));
console.log(/(Joe +)(?=Doe)/.test("Joe Doe"));
console.log(/(Joe) +(?=Doe)/.test("Joe Bob"));
["33v", "45d"]
["4@jK"]
true
false
67. Common use cases
• Determine valid phone number
• Determine valid e-mail address
• Extract specific values from a string
• Parse CSV data
• Check for existence of content
69. Good to know
• Understand how DOM and JS interact
• Memory leaks between DOM and JS
• DOM transactions are costly
• Patterns in JS can help with DOM
• Patterns for DOM better use jQuery
70. Observer pattern
• Used to decouple cause and effect
• Trigger event unaware of effect
• Event handlers respond to event
• Similar to publish / subscribe pattern
• Many frameworks use it already
71. MVC pattern
• Model, View, Controller
• Separation of concerns
• Comes in different flavors
• Ideal candidate to mix with Observer
• Promotes use of templates
73. Coding Style
• Standardization improves quality
• Uses Java’s naming convention de facto
• Camel casing of identifiers
• Uppercase first letter to suggest a Class
• Alphabetize function definitions
75. Garbage collection
• Uses a mark and sweep strategy
• Assign a null value to help clean up
• Use of delete to help clean up
• Consistency helps with performance
• Proprietary GarbageCollect() not so good
77. Use of features
• Breakpoints
• Call stack
• The debugger statement
• Acceptable uses of alert()
• Acceptable uses of console API
78. Console API
• Bad idea to override console
• Use of console.log() to navigate objects
• Use of different console methods
• Using console.warn() shows up in-code
• Easy to shim
79. Connect
• Thank you for your time
• Connect with me on LinkedIn
• Join the Hardcore JavaScript community
• Read my blog
• Contact me via e-mail
Editor's Notes
The two primitives that are mutable in 1.5 are undefined and null. This was fixed in ES5.
The two primitives that are mutable in 1.5 are undefined and null. This was fixed in ES5.
A developer is not expected to remember what types are resolved to as it may vary in engines - always rely on engine to check.
Example is typeof /s/ === ‘function'; works in FF as per ES5 but not in Chrome
What is type coercion?
We will explore these in an upcoming slide.
There is also try/catch you could use to control the logic flow.
Ternary operator is also known as conditional operator.
Each check takes resources
Move function calls outside of condition and reference its output
We will explore refactor in next slide
Here we see how we refactored it
Only one function call
Function call cached to var
Check against var
Nest conditions to reduce condition check cycles
Here we show boolean operator to be used for default values in a function
Compare nested ternary construct and if construct
Tell there will be more on prototype later
Give a brief intro to prototype
It inherits the global Object prototype
Here we see it does not have its own prototype
Here we see how a literal inherits the Object prototype
Here we see how delete works on a property of an object
We also see that delete returns true
Internally there is a Variable Object that keeps track of what is delete-able
Show example of prototype on instantiated object
Notice the prototype is extended on the class - not the object
We can see myName is not accessible via x
Think keys are unique? They are this simply shows the prototype chain - not the actual keys of the object.
x.test would return what? Test 3 - why?
Question: How would we get x.test to return Test 1 again?
Mention IE crashes if key is reserved keyword
Proxy API will allow for catch-all functions
We will get back to scope chain a little later
Dynamic scope not available but dynamics present in the sense of cannot determine lexical scope at parsing time.
Here we want to show the scope chain and how it resolve “a”
Here we can see that when we don’t nest, we can still avoid nesting by simply passing arguments - which is a better way to go.
Show how the function B 2 is not a source-element so it should not hoist. Chrome shows B 2 and FireFox shows B 1
Here we see that referenceAx returns a reference to the Ax() function but calling Ax() won’t work because Ax is only recognized from within Ax()
Thus referenceAx !== referenceAx() in spite of them looking identical
If we would pass by reference, the last statement (objectliteral = undefined) would wipe out the objectliteral - but it doesn't! Interestingly enough when we do modify a key, it will properly modify the key of the given object. This suggests that the parameter contains the value of the address of the object so that when we reference the key, we go to the address and modify the given key. However, if we assign a new value to the variable that contains the address we simply overwrite the value with our new value - effectively losing the reference to the object.
Another way to illustrate this point, look at the example
Inspect the b variable when debugger statement hits.
This will show that it’s been GC’d.
Add a reference to b and we will see it doesn’t GC it.
Two types of expressions are those that assign a value and those that just have a value.