-
Notifications
You must be signed in to change notification settings - Fork 0
JS workshop notes
Misconceptions:
- Javascript is an interpreted language (not really?)
- Hoisting (variables, declared functions are not really hoisted)
- Javascript is compiled. A bug on line 10 can prevent lines 1-9 from running. This wouldn't happen in a truly interpreted language.
- After Java is compiled into byte-code, it needs to run through the Java Virtual Machine (JVM) to be interpreted
- The JS engine does something similar
- The JS compiler is one of the most sophisticated compilers in existence (one reason is due to JS being dynamically typed, "type inferencing")
"Where to look for things"
Javascript has function scope only?
/*
this is treated as two different statements
var foo is handled at compilation:
compiler looks for formal declarations ("var")
compiler asks scope manager (global scope): do you have anything for "foo" identifier?
scope manager says "no", so registers it in memory
foo = "bar" is handled at execution time
*/
var foo = "bar";
/*
same thing happens, but in new scope
*/
function bar() {
var foo = "baz"; // "shadowing" means we can never access global "foo" in this scope
}
/*
formal declaration is the same for parameter "foo"
JS will not allow multiple parameters of the same name
"bam" doesn't get handled during compile time, it gets handled at execution time
because var bam; could appear later in the code, so the compiler leaves it alone
*/
function baz(foo) {
// hey scope of baz, I have an LHS reference of foo, do you have it in memory?
// yes! from the parameter
foo = "bam";
// hey scope of baz, have you heard of bam?
// nope. let's go up one level (in this case to global scope) and ask
// global scope will throw an error in strict mode,
// but in lazy mode it will create a reference in the global scope, not local
bam = "yay"; // not a formal declaration
}
"Hoisting":
var foo = "test";
function bar() {
foo = "huh?"; // not assigned to global scope foo!
var foo = "in bar";
}
// is the same as:
function bar() {
var foo; // because this is done at compilation, not really "hoisted"
foo = "huh?";
foo = "in bar";
}
Undeclared and undefined are not synonyms! Undefined values have been formally declared.
Always write in strict mode!
Always prefer named function expressions to anonymous function expressions!
- Better for using function within function (recursion)
- Better for debugging, looking through stack traces
- Makes code more readable, self-documenting
This is one of the downsides of arrow functions, since these are always anonymous.
Why use a linter? Only for code style, not for bugs. Correct code is handled by the compiler, and should be handled by tests.
Just don't use eval
(duh)... mainly for performance reasons. If the JS engine finds eval
anywhere in a file, it disables a ton of optimizations.
IIFE can be written like so:
void function IIFE() {
// don't worry about polluting global scope!
}();
Good practice:
(function IIFE(global, $) {
// cleans up global/jQuery calls/assignments
})(window, jQuery);
Function hoisting is great for readability:
// Look at the top to see what gets executed first!
init();
// Function declarations all get hoisted
function init() {
foo();
bar();
}
function foo() {
}
function bar() {
}
Two aspects of module pattern:
- Outer enclosing function that runs at least once
- Function should return an object that contains at least one inner function (using closure)
- Expose a minimal public API (hide implementation details)
What is a closure?
- When a function remembers the outer lexical variables it has access to
- When you take that function and put it in a different scope
- When a function remembers its lexical scope, while executing in a different scope
How long does its scope stay around?
- (I forget)
What happens when you use the new
keyword?
- Brand new empty object
- Gets linked to another object
- Newly created and linked object gets passed in as the
this
keyword - Returns its
this
(unless function explicitly returns an object)
- A constructor makes an object linked to its own prototype
- Don't be misled by the
constructor
keyword! - New instances don't receive copies from their "constructor", so properties can be added retroactively but updating the prototype
What is a constructor call?
- The
new
keyword put in front of any function
What is Prototype and where does it come from?
- It's a reference linkage between two objects, which occurs when the object is created
How does Prototype affect the behavior of an object?
- Whenever we ask for a property on an object, we find it by walking up the prototype chain
How do we find out where an object's Prototype points to?
-
Object.getPrototypeOf
,__proto__
,constructor.prototype
Better than referring to it as "inheritance"
Use Object.create
instead!
var Widget = {
setDimensions: function(width,height) {
this.width = width || 50;
this.height = height || 50;
this.$elem = null;
},
buildDOMElement: function($where) {
if (this.$elem) {
this.$elem.css({
width: this.width + "px",
height: this.height + "px"
}).appendTo($where);
}
}
};
var Button = Object.assign(Object.create(Widget), {
configure: function(width, height, label) {
// delegated call
this.setDimensions(width, height);
this.label = label || "Default";
this.$elem = $("<button>").text(this.label);
},
activate: function($where) {
// delegated call
this.buildDOMElement($where);
this.$elem.click(this.onClick.bind(this));
},
onClick: function(evt) {
console.log("Button '" + this.label + "' clicked!");
}
});
How is Javascript's Prototype chain not like traditional/classical inheritance?
- It's a link up instead of a copy down
What does Prototype delegation mean and how does it describe object linking in JS?
- Intentionally separate objects, so we can combine them as needed (virtual composition)
What are the benefits of the "behavior delegation" design pattern?
- Simpler code, easier to test
- Simpler design pattern
What are the tradeoffs of using Prototype?
- Anything on the prototype is public (this is why modules are good)