// ----------------------------------------
// JAVASCRIPT UNDERSTANDING THE WEIRD PARTS
// ----------------------------------------
/* Syntax Parsers, Execution Contexts and Lexical Environments
Syntax Parser: a program that reads your code and determines what it does and if its grammar is valid
Your Code -> Computer Instructions (By a Compiler or Interpreter)
Lexical Environment: Where something sits physically in the code you write
The variable sits lexically inside a function
Execution Context: A wrapper to help manage the code that is running
Contains your running code and can contain other things beyond this
Name/Value Pairs and Objects
name/value pair: a name which maps to a unique value [name is assigned to a value in any given context]
address = '100 Main St' [name is address value is "100 Main St"]
Object is a collection of name value pairs
*/
/* The Global environment and the global Object
Global = "not inside a function"
The global execution context [the thing thats accessible to everything and everywhere in your code]
JS engine creates Global Object and this
blank.js file still creates an execution context [this defaults to Window]
There's always a global object in JS execution context
An execution context was created at the globa level [available to all code running inside that lexical environment]
Special variable [global object] called this = window was created
*/
var a = "Hello World";
function b() { }
/* a and b are added to the global object [window in browsers] */
/* The Execution Context and Hoisting
*/
b();
console.log(a);
var a = 'Hello World';
function b() {
console.log('Called b!');
}
/* Called b!
undefined
Function still ran even though it was called before it was declared
*/
/* Execution Context is created in 2 phases
CREATION phase [this is always created in an execution context]
It recognizes where you created variables and function
It sets up the memory space for variables and functions "Hoisting" variables = undefined functions exist in their entirety
Before your code begins to be executed line by line the JS engine has already put them in memory space [name and code]
Before the execution phase it adds an undefined placeholder
Functions are put into memory in their entirety
We have access to var/funcs because they are sitting in memory before they are declared in your code
*/
var a;
console.log(a); // undefined
if (a === undefined) {
console.log('a is undefined');
}
else {
console.log('a is defined');
}
/* Javascript and undefined
Undefined a special value keyword that JS has internally that proves the value has not been set
if you don't define A you wil get an Uncaught Reference error
Never do a = undefined; its a little dangerous
Makes it hard to tell if you set it or if JS did
*/
/* Single threaded: one command at a time
JS isnt the only thing in the browsers
Synchronous: one at a time and in order
AJAX A=Asynchronous
JS is synchronous
*/
/* Function invocation and the execution stack
Invocation: Running or calling a function by using ()
*/
function b() {
}
function a() {
b();
}
a();
/*
Global execution context is created
Creation phase create this [global object]
Attach functions in the memory space a and b will be in memory
once it hits a() it creates execution context and puts it on the execution stack
Whichever one is on top is the one that is currently running
It goes through the create phase, and creates execution context as they are found
Once it finishes its popped off of the stack
*/
/* Variable Environment: Where the variables live and how they relate to each other in memory
*/
function b() {
var myVar;
}
function a() {
var myVar = 2;
b();
}
var myVar = 1;
a();
/* Global execution context is created
myVar = 1
invocation of a -> new execution context is created for a
myVar = 2
invocation of b -> new execution context is created for b
myVar = undefined
*/
/* The Scope Chain
JS engine looks in more than the current execution context
B's and A's outer environment is global execution context
If it cant find that variable it will look at the outer reference
b sits lexically in global
a sits lexically in global
it couldnt find myVar so it went down the scope chain to outer reference [global]
*/
function b() { // lexically in the global environment object
console.log(myVar);
}
function a() {
var myVar = 2;
b();
}
var myVar = 1;
a();
// outputs 1
function a() {
function b() {
console.log(myVar);
}
var myVar = 2;
b()
}
var myVar = 1;
a();
/* because b is inside a, it changed the outer reference to a [down the scope chain instead of global go to a] */
/* Scope, ES6 and let
Scope: where a variable is available in your code and if it is truly the same variable or a new copy
let = var with benefits [block scoping]
*/
if (a > b) {
let c = true; // its still in memory but cant be used outside the block {}
}
/* Asynchronous callbacks
Asynchronous: more than one a time
Browser
Javascript engine hooks into other parts of the browser
Rendering engine
HTTP request
When the stack is empty then JS looks at the Event Queue
Event queue wont be processed until the execution stack is empty
*/
// long running function
(function() {
function waitThreeSeconds() {
var ms = 3000 + new Date().getTime();
while (new Date() < ms) {}
console.log('finished function');
}
function clickHandler() {
console.log('click event');
}
// listen for the click event
document.addEventListener('click', clickHandler);
waitThreeSeconds();
console.log('finished execution');
})();
/*
Types and Javascript
Dynamic Typing: You dont tell the engine what type of data a var holds, it figures it out while your code is running
variables can hold different types of values because its all figured out during execution
*/
(function() {
// Static Typing
// bool isNew = 'hello'; // an error
// Dynamic Typing
var isNew = true; // no errs
isNew = 'yup!';
isNew = 1;
})();
/* 6 Primitive types in JS: a type of data that represents a single value, that is not an object.
undefined represents a lack of existence (dont set to undefined let engine use it)
null represents lack of existence (you can set a variable to this)
boolean true or false
number floating point number (there's always some decimals) unlike other languages theres only one number type can make math WEIRD
string a sequence of characters (' and " can be used)
symbol used in es6 (constructed and not fully supported)
*/
/* Operators: are special functions that are syntactically (written) differently, generally they take 2 parameters and return one result
+ addition operator (function)
- subtraction operator (function)
> greater than operator (function) boolean
< less than operator (function) boolean
* multiply operator (function)
/ divide operator (function)
% modulus (remainder) operator (function)
= assignment operator (function)
== equality operator (function) boolean
=== strict equality operator (function) boolean
!= not equal operator (function) boolean
!== strict not equal operator (function) boolean
Uses In fix notation (operator sits between parameters) instead of prefix notation or postfix notation
*/
(function(){
var a = 3 + 4;
console.log(a);
})();
/* Operator precendence and associativity
Precedence: Which operator function gets called first when functions are called in order of precendence (higher precedence wins)
Associativity: What order operator functions get called in: left to right or right to left when functions have same precedence
Precedence Operator type AssociativIndividual operator
20 Grouping n/a ( … )
19 Member Access left-to-ri… . …
Computed Member Access left-to-ri… [ … ]
new (with argument list) n/a new … ( … )
18 Function Call left-to-ri… ( … )
new (without argument list)right-to-lnew …
17 Postfix Increment n/a … ++
Postfix Decrement n/a … --
16 Logical NOT right-to-l! …
Bitwise NOT right-to-l~ …
Unary Plus right-to-l+ …
Unary Negation right-to-l- …
Prefix Increment right-to-l++ …
Prefix Decrement right-to-l-- …
typeof right-to-ltypeof …
void right-to-lvoid …
delete right-to-ldelete …
15 Exponentiation right-to-l… ** …
14 Multiplication left-to-ri… * …
Division left-to-ri… / …
Remainder left-to-ri… % …
13 Addition left-to-ri… + …
Subtraction left-to-ri… - …
12 Bitwise Left Shift left-to-ri… << …
Bitwise Right Shift left-to-ri… >> …
Bitwise Unsigned Right Shifleft-to-ri… >>> …
11 Less Than left-to-ri… < …
Less Than Or Equal left-to-ri… <= …
Greater Than left-to-ri… > …
Greater Than Or Equal left-to-ri… >= …
in left-to-ri… in …
instanceof left-to-ri… instanceof …
10 Equality left-to-ri… == …
Inequality left-to-ri… != …
Strict Equality left-to-ri… === …
Strict Inequality left-to-ri… !== …
9 Bitwise AND left-to-ri… & …
8 Bitwise XOR left-to-ri… ^ …
7 Bitwise OR left-to-ri… | …
6 Logical AND left-to-ri… && …
5 Logical OR left-to-ri… || …
4 Conditional right-to-l… ? … : …
3 Assignment right-to-l… = …
… += …
… -= …
… **= …
… *= …
… /= …
… %= …
… <<= …
… >>= …
… >>>= …
… &= …
… ^= …
… |= …
2 yield right-to-lyield …
yield* right-to-lyield* …
1 Spread n/a ... …
0 Comma / Sequence left-to-ri… , …
*/
(function(){
var a = 3 + 4 * 5;
console.log(a); // 23
})();
(function() {
var a = 2, b = 3, c = 4;
a = b = c;
console.log(a); // 4
console.log(b); // 4
console.log(c); // 4
})();
// The associativity of the assignment operator is right to left (starts with c)
/* Grouping () gets run first because it has highest precedence (20) */
(function() {
var a = (3 + 4) * 5; // 35
})();
/* Coercion: converting a value from one type to another
*/
(function() {
var a = 1 + '2'; // 12
console.log(a);
})();
/* Comparison Operators
Less than: left to right
*/
(function() {
console.log(3 < 2 < 1); // 3 is less than 2 is false become coerced to 0 is less than 1
console.log(false < 1); // true
console.log(1 < 2 < 3); // 1<2 true, true=1 < 3 returns true
false == 0; // true
null == 0; // false
null < 1; // true
"" == 0; // true
"" == false; // true
})();
// coerces the boolean to a number (0)
(function() {
Number(false); // 0
Number(true); // 1
Number(undefined); // NaN no way to convert to number
Number(null); // 0
var a = 0;
var b = false;
if (a == b) {
console.log('They are equal');
} else {
console.log('Nope not equal');
} // they are equal
if (a === b) {
console.log('They are equal');
} else {
console.log('Nope not equal');
} // nope not equal
})();
// 99% of the time try to use === strict equality operator (function)
/* Existence and Booleans*/
(function(){
Boolean(undefined); // false
Boolean(null); // false
Boolean(""); // false
Boolean(0); // false
false || true; // true
var a;
// goes out to the internet and looks for a value
if (a || a === 0) {
console.log('something is there');
} // if a is undefined, null or "" it will, using coercion to check if a is nothing
})();
/* Default Values
ES6 is going to let you set default value
*/
(function(){
function greet(name) {
name = name || ''; // set default value
console.log(name);
console.log('Hello ' + name);
}
greet('Aaron');
greet(); // name is undefined
})();
// OR is special, it will return the first one that coerces to true
(function() {
undefined || "hello"; // hello
true || false; // true
})();
lib1.js
var libraryName = 'Lib 1';
lib2.js
var libraryName = 'Lib 2';
app.js
console.log(libraryName); // Lib 2
lib2.js
window.libraryName = window.libraryName || 'Lib 2'; // lets Lib 1 exist or takes it if it doesnt
/* Object and Functions
Objects and the Dot
A collection of names and values (properties and methods)
Primitive "property"
Object "property"
Function is a "method" -- when its sitting on an object
*/
(function(){
var person = new Object(); // var person = {};
person["firstname"] = "Aaron"; // [] computed member access operator. string primitive property
person["lastname"] = "Rosario";
var firstNameProperty = "firstname";
console.log(person); // Object {firstname: "Aaron", lastname: "Rosario"}
console.log(person[firstNameProperty]); // Aaron
// . member access operator is more common
console.log(person.firstname);
console.log(person.lastname);
person.address = new Object(); // object inside object
person.address.street = '111 Main St.'; // the associativity of the member access operator is left to right
person.address.city = 'Raleigh';
person.address.state = 'NC';
console.log(person.address.street);
console.log(person["address"]["state"]); // same thing
})();
// computed member access is better for dynamically named string
/* Objects and Object Literals
*/
(function(){
var person = {
firstname: 'Aaron',
lastname: 'Rosario',
address: { // address property that is an object itself
street: '120 St Albans Dr',
unit: 'Apt 585',
city: 'Raleigh',
state: 'NC'
}
};
function greet(person) {
console.log('Hi ' + person.firstname);
}
greet(person);
greet({ firstname: 'John', lastname: 'Doe' }); // creating an object on the fly
greet({
firstname: 'John',
lastname: 'Doe'
});
console.log(person); // Object: { firstname: "Aaron", lastname: "Rosario" , address: Object }
})();
/* Faking Namespaces
Namespace: a container for variables and functions typically to keep vars and functions with same name separate
*/
(function(){
var greet = 'Hello!';
var greet = 'Hola!';
console.log(greet); // Hola!
var english = {};
var spanish = {};
english.greet = 'Hello!'; // these dont collide, kept separate, using an object as a namespace
spanish.greet = 'Hola!';
})();
/* JSON vs Object Literals
JavaScript Object Notation is inspired by JS object literal
XML (wasted download bandwidth):
JSON is more strict, properties have to wrapped in quotes:
{
"firstname": "Mary",
"isAProgrammer": true
}
*/
(function(){
var objectLiteral = {
firstName: 'Mary',
isAProgrammer: true
};
var jsonValue = JSON.parse('{ "firstName": "Mary", "isAProgammer": true}');
console.log(objectLiteral);
console.log(JSON.stringify(objectLiteral));
console.log(jsonValue);
})();
/* Functions are objects
First class functions: Everything you can do with other types you can do with functions. (assign them to vars pass them around create them on the fly)
Function is a special type of object it has hidden special properties
You can attach properties and methods to a function (primitives, objects, functions)
Name [optional, can be anonymous]
Code property [actual code of the function] Invocable with ()
*/
(function(){
function greet() { // name property greet, code property below invocable with ()
console.log('hi');
}
greet.language = 'english'; // added a property to a function
console.log(greet); // the code property of the function
console.log(greet.language); // english
})();
/* function statements and function expressions
expression: a unit of code that results in a value (it doesnt have to save to a value)
statement just does work expression returns a value
*/
(function(){
var a;
a = 3; // valid expression
1 + 2; // valid expression returns 3 but isnt saved to a var
a = { greeting: "hi" }; // valid expression
if (a == 3) { // if statement with expression a == 3
}
greet(); // its hoisted so its available before it is declared in a statement
function greet() { // function statment -- doesnt result in a value, its in memory but it doesnt return a value until it is executed
console.log('hi');
}
//anonymousGreet(); // uncaught type error undefined is not a function, ReferenceError wont get hoisted because its a function expression
var anonymousGreet = function() { // function expression -- because it results in an object its name is anonymous its referenced by variable name, code property is the same
console.log('hi');
}
anonymousGreet();
function log(z) {
console.log(z);
z(); // runs the z function that was passed in
}
log(function() { // passes a function to a function
console.log('hi');
});
})();
/* by value vs by reference
by value
a // 0x001 location in memory
b = a // 0x002 copy of primitive value
All primitive values are by value
by reference
a = {} // 0x001
b = a // 0x001
All objects interact by reference when setting them equal or passing through functions
Mutate: to change something
"immutable" means it cant be changed
*/
(function(){
// by value (primitives)
var a = 3;
var b;
b = a; // by value because integret primitive type, new spot in memory for b, copies
a = 2; // doesnt affect b because they are in different memory spots
console.log(a);
console.log(b);
// by reference
var c = { greeting: 'hi' };
var d;
d = c; // these are objs rather than set up d in a memory space use c's memory address
c.greeting = 'hello'; // mutate
console.log(c); // Object { greeting: "hello" }
console.log(d); // Object { greeting: "hello" }
// by reference (even as parameters)
function changeGreeting(obj) {
obj.greeting = 'Hola'; // mutate
}
changeGreeting(d);
console.log(c); // object { greeting: "Hola" }
console.log(d); // object { greeting: "Hola" }
// equals operator sets up new memory space (new address)
c = { greeting: 'howdy' }; // brand new object by this object literal syntax (new memory address)
console.log(c); // object { greeting: "howdy" }
console.log(d); // object { greeting: "Hola" }
})();
/* Object, functions and 'this'
When a function is invoked an execution context is created and put on the execution stack (CREATION phase)
Variable environment
Outer lexical environment
this -- points at a different object depending on how the function is invoked
*/
(function(){
console.log(this); // window object
function a() {
console.log(this);
this.newvariable = 'hello'; // window.newvariable
}
var b = function() {
console.log(this);
}
a(); // global window object
console.log(newvariable); // newvariable went crashing into the global namespace
b(); // global window object
var c = {
name: 'The c object',
log: function() {
var self = this; // some people use var that = this;
self.name = "Updated c object"; // mutated the object that contains me
console.log(self); // Object { name: "The c object" }, youre attached to an object so im going to point to the object that contains you
var setname = function(newname){
self.name = newname;
}
setname('Updated again! the c object'); // Some people think this is a bug when you use this, because name was added to the global window object, instead setup self var
console.log(self);
}
}
c.log();
})();
/* Arrays collections of anything
0 based
Dynamically typed
*/
(function() {
var arr = new Array();
var arr = [1, false, 3];
var arr = [
1,
false,
{
name: 'Aaron',
address: '120 St Albans'
},
function(name) {
var greeting = 'Hello ';
console.log(greeting + name);
},
"hello"
];
console.log(arr);
arr[3](arr[2].name); // neat thing about dynamic typing
})();
/* arguments and Spread
Execution Context is created (FUNCTION)
sets up a variable environment
sets up this
sets up outer environment
sets up arguments
Arguments: the parameters you pass to a function
Javascript gives you a keyword of the same name which contains them all
Arguments will become deprecated, the new thing is called a spread parameter
...other separate array of misc args
*/
(function() {
// in es6: function greet(firstname = 'john', lastname = 'doe', language = 'en')
function greet(firstname, lastname, language) { // arguments werent passed in sets them to undefined
language = language || 'en'; // default value if undefined
if (arguments.length === 0) {
console.log('Missing parameters!');
return;
}
console.log(firstname);
console.log(lastname);
console.log(language);
console.log(arguments); // array like
}
greet(); // returns 3 undefined; no parameters, js dont care, other languages do
greet('John'); // returns John and 2x undefined because of hoisting
greet('John', 'Doe', 'es'); // returns John, Doe and es
})();
/* function overloading - JS doesnt have */
(function() {
function greet(firstname, lastname, language) {
language = language || 'en';
if (language === 'en') {
console.log('Hello ' + firstname + ' ' + lastname);
}
if (language === 'es') {
console.log('Hola ' + firstname + ' ' + lastname);
}
}
function greetEnglish(firstname, lastname) {
greet(firstname, lastname, 'en');
}
function greetSpanish(firstname, lastname) {
greet(firstname, lastname, 'es');
}
greetEnglish('John', 'Doe');
greetSpanish('John', 'Doe');
})();
/* Syntax Parsers
Reads your code and determines if its valid and what it is trying to do
r,e,t,u,r,n character by chracter
; terminating character
return; valid
*/
/* Automatic semicolon insertion
Semicolons are optional
return\n hey you cant use a return after this statement we will put a semicolon there for you return;
Good practice is to always put your curly braces on the same line as your keywords to avoid this problem
*/
(function(){
function getPerson() {
// return // because theres a new line it auto inserts ;
return { // this works
firstname: 'Aaron'
}
}
console.log(getPerson()); // undefined because of automatic semicolon insertion
})();
/* Whitespace: invisible characters that create literal space in your written code (carriage returns, tabs, spaces)
*/
(function(){
// first name of the person
var firstname,
// last name of the person
lastname,
// language of the person can be 'en' or 'es'
language;
var person = {
// I can even add comments into object literals
firstname: 'John',
lastname: 'Doe'
}
console.log(person);
})();
/* Immediately Invoked Function Expressions (IIFE)s
Typically uses the grouping operator () just to trick the syntax parser
(function() {
})();
*/
(function(){
function greet(name) { // function statement
console.log('Hello ' + name);
}
greet();
var greetFunc = function(name) { // function expression
console.log('Hello ' + name);
};
greetFunc();
var greeting = function(name) {
console.log('Hello ' + name);
}('Aaron'); // IIFE
var firstname = "Aaron";
(function(name) {
var greeting = 'Inside IIFE: Hello';
console.log(greeting + ' ' + name);
})(firstname);
})();
/* IIFEs and safe code
*/
(function(global, name) {
var greeting = 'Hello'; // only exists in this IIFE
global.greeting = 'Hello'; // if you want this available to global window scope
})(window, 'John'); // IIFE
/* Understanding closures
A feature of the JS engine
Global execution context
greet() execution context
whattosay is sitting in its variable environment
greet() is popped off the stack
the memory of greet()'s execution context still exists
*/
(function() {
function greet(whattosay){
return function(name) {
console.log(whattosay + ' ' + name);
}
}
greet('Hi')('Aaron');
var sayHi = greet('Hi');
sayHi('Aaron');
})();
/* Understanding closures part 2
Global execution content buildFunction(), fs
buildFunctions() get execution context i=3 arr=[f0, f1, f2]
popped off of stack after for loop
i is 3 by the time you execute all these functions
*/
(function() {
function buildFunctions() {
var arr = [];
for (var i = 0; i < 3; i++) {
arr.push(
function() {
console.log(i); // isnt being executed right here
}
)
}
return arr;
}
var fs = buildFunctions();
fs[0](); // 3 execution context is created, uses the last execution context and outer reference
fs[1](); // 3 all pointing at the same point in the scope chain buildFunction
fs[2](); // 3 they are all executed later then when they were assigned and give parents value
})();
/* Function Factories */
function makeGreeting(language) {
return function(firstname, lastname) {
if (language === 'en') {
console.log('Hello ' + firstname + ' ' + lastname);
}
if (language === 'es') {
console.log('Hola ' + firstname + ' ' + lastname);
}
}
}
var greetEnglish = makeGreeting('en');
var greetSpanish = makeGreeting('es');
greetEnglish('John', 'Doe');
greetSpanish('John', 'Doe');
/* Closures and Callbacks */
/* setTimeout uses a closure to still have access to the variables at the time of execution */
/* Callback Function: a function you give to another function, to be run when the other funciton is finished
so the function you call (ie invoke) 'calls back' by calling the function you gave it when it finishes */
function sayHiLater() {
var greeting = 'Hi!';
setTimeout(function() {
console.log(greeting);
}, 3000);
}
sayHiLater();
// jQuery uses function expression and first class functions
$("button").click(function() {
});
function tellMeWhenDone(callback) {
var a = 1000;
var b = 2000;
callback(); // the 'callback' it runs the function I give it!
}
tellMeWhenDone(function() {
console.log('I am done');
})
tellMeWhenDone(function() {
console.log('All Done');
});
/* call(), apply(), bind()
All functions have access to call() apply() and bind() methods.
They affect the 'this' context.
*/
var person = {
firstname: 'John',
lastname: 'Doe',
getFullName: function() {
var fullname = this.firstname + ' ' + this.lastname; // this context is person
return fullname;
}
}
var logName = function(lang1, lang2) {
console.log('Logged: ' + this.getFullName()); // this is on the globalobject because its not a method
console.log('Arguments: ' + lang1 + ' ' + lang2);
console.log('-------------')
}
var logPersonName = logName.bind(person); // bind copies logName and runs it with person context
logPersonName();
logname.call() = logname(); // bind copies the funciton, call executes it
logName.call(person, 'en', 'es'); // specifying your this context and function parameters
logName.apply(person, ['en', 'es']); // exact same thing except args are passed as array -- only difference between call and apply
// function borrowing
var person2 = {
firstname: 'Jane',
lastname: 'Doe'
}
person.getFullName.apply(person2); // borrow the method from person object for person2
// function currying
function multiply(a,b) {
return a*b;
}
var multiplyByTwo = multiply.bind(this, 2); // copy multiply function and set a permanent value for the first parameter
multiplyByTwo(4); // 8
/* Function Currying, creating a copy of a function by with some preset parameters, very useful in mathematical situations */
/* Functional Programming
JS is more like LISP, Schema, ML (first class functions, functions as parameters/returns)
*/
var mapForEach(arr, fn) {
var newArr = [];
for (var i=0; i < arr.length; i++) {
newArr.push(fn(arr[i]);
}
return newArr;
}
var arr1 = [1,2,3];
var arr2 = mapForEach(arr1, function(item) {
return item * 2;
});
var checkPastLimit = function(limiter, item) {
return item > limiter;
}
var arr4 = mapForEach(arr1, checkPastLimit.bind(this, 1));
console.log(arr4);
var checkPastLimitSimplified = function(limiter) {
return function(limiter,item) {
return item > limiter;
}.bind(this, limiter);
};
varr arr5 = mapForEach(arr1, checkPastLimitSimplified(1));
console.log(arr5);
console.log(arr2);
var arr2 = [];
for (var i=0; i < arr1.length; i++) {
arr2.push(arr1[i] * 2);
}
console.log(arr2); // [1,2,3][4,5,6]
/* Functional Programming Part 2
underscore.js
*/
var arr6 = _.map(arr1, function(item) { return item * 3});
var arr7 = _.filter([2,3,4,5,6,7], function(item){ return item % 2 === 0});
/* Object-oriented Javascript and Prototypal Inheritance
Inheritance: One object gets access to the properties and methods of another object.
Classical Inheritance: C#, Java
Verbose: friend, protected, private, interface
Prototypal Inheritance: Simple, flexible, extensible, easy to understand
Understanding the prototype
obj -> prop1 (obj.prop1)
obj -> proto {} (reference to object property proto, where it gets its methods and props from)
obj -> proto {} -> prop2 (obj.prop2) (prop2 isnt on our object its on its prototype)
obj -> proto {} -> proto {} -> prop3 (obj.prop3) (its down the prototype chain)
Scope chain is about looking for where we have access to a variable prototypal chain is about where we have access to a method or property in a sequence of objects
obj2 -> proto {} (obj2.prop2 is referencing obj proto{})
*/
var person = {
firstname: 'Default',
lastname: 'Default',
getFullName: function() {
return this.firstname + ' ' + this.lastname;
}
}
var john = {
firstname: 'John',
lastname: 'Doe'
}
// dont do this ever, performance issues
john.__proto__ = person; // john now inherits from person
console.log(john.getFullName()); // will see that john doesnt have that method and search its prototype, if not there then thats prototype
var jane = {
firstname = 'Jane'
}
jane.__proto__ = person;
console.log(jane.getFullName());
/* Reflection and Extend
Reflection: An object can look at itself, listing and changing its properties and methods
*/
var person = {
firstname: 'Default',
lastname: 'Default',
getFullName: function() {
return this.firstname + ' ' + this.lastname;
}
}
var john = {
firstname: 'John',
lastname: 'Doe'
}
john.__proto__ = person;
for (var prop in john) {
if (john.hasOwnProperty(prop)){ // only log if its on the object not if its on the prototype chain
console.log(prop + ': ' + john[prop]);
}
}
// underscore's extend
var jane = {
address: '111 Main St.',
getFormalFullName: function() {
return this.lastname + ', ' + this.firstname
}
}
var jim = {
getFirstName: function() {
return firstname;
}
}
_.extend(john, jane, jim); // combines jim and janes props/methods to john
// .extends in es6 will extend the prototype
/* Building Objects
Function constructors 'new' and the history of JS
*/
var john = new Person(); // Made for Java Devs
function Person(firstname, lastname) {
console.log(this);
this.firstname = firstname;
this.lastname = lastname;
console.log('This function is invoked');
}
Person.prototype.getFullName = function() {
return this.firstname + ' ' + this.lastname;
}
var john = new Person('John','Doe'); // Inherits Person.prototype
console.log(john);
var jane = new Person('Jane', 'Doe'); // Inherits Person.prototype
console.log(jane);
// You can add stuff to the prototype later and access it through the prototype chain
Person.prototype.getFormalFullName = function() {
return this.lastname + ', ' + this.firstname;
}
john.getFormalFullName();
/* Function constructors: a normal function that is used to construct objects
The 'this' variable points a new empty object and that object is returned from the function automatically
Functions are a special type of object with the following prototypes:
NAME (optional can be anonymous)
CODE (Invocable())
prototype (used only by the 'new' operator, only when used as a constructor)
Adding methods to the prototype is better than adding them in the constructor function because it saves space (methods in the constructor function are copied to every object instance)
Its better to go up the prototype chain than copying a method to every object instance
*/
/* Dangerous Aside: 'New' and functions
If you forget to put the new keyword when instantiating a constructor function it will execute the function
Use a capital letter for constructor functions so you can tell that it should have the 'new' keyword in front of it instead of executing it
*/
/* Conceptual Aside: Built-in function constructors */
var a = new Number(3); // Number {[[PrimitiveValue]]: 3} gets the Number primitive's prototype
var a = new String("Aaron"); // String {0: "A", 1: "a", 2: "r", 3: "o", 4: "n", length: 5, [[PrimitiveValue]]: "Aaron"}
var a = new Date('12/16/2016'); // Fri Dec 16 2016 00:00:00 GMT-0500 (Eastern Standard Time)
String.prototype.isLengthGreaterThan = function(limit) { // ALL STRINGS INSTANTLY HAVE ACCESS TO THIS METHOD
return this.length > limit;
}
console.log("Aaron".isLengthGreaterThan(3));
Number.prototype.isPositive = function() {
return this > 0;
}
/* 3.isPositive(); // Errors because JS does not auto convert integers to their primitive for you
var a = new Number(3); // now it will work now that it is wrapped with primitive prototypes
Not always the best option, try to avoid this unless necessary
------------------
*/
var a = 3; // is a primitve
var b = new Number(3); // is an object created with
var a == b; // true due to coercion
var a === b; // false due to b being an object (thanks new keyword)
/* Preferred to use literals and the actual primitive values instead of the built in constructor functions for primitives
Arrays and for..in
------------------
Arrays are objects
*/
Array.prototype.myCustomFeature = 'cool!'; // this will add this property and value to all arrays
var arr = ['John', 'Jane', 'Jim'];
for (var prop in arr) {
console.log(prop + ': ' + arr[prop]);
}
/* so iterate through arrays with for (i = 0; i < arr.length; i++)
*/
/* Object.create and Pure Prototypal Inheritance */
// Polyfill: code that adds a feature which the engine may lack.
if (!Object.create) { // older JS engine polyfill
Object.create = function(o) {
if (arguments.length > 1) {
throw new Error('Object.create implementation only accepts the first parameter');
}
function F() {}
F.prototype = o;
return new F();
}
}
var person = {
firstname: 'Default',
lastname: 'Default',
greet: function() {
return 'Hi' + ' ' + this.firstname; // objects dont get execution context so you need to add this
}
}
var aaron = Object.create(person);
aaron.firstname = 'Aaron';
aaron.lastname = 'Rosario';
console.log(aaron);
/*
You can change the prototype along the way
*/
/* ECMA Script 6 and Classes
In other languages classes are not objects
THIS IS AN OBJECT
*/
class Person {
constructor(firstname, lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
greet() {
return 'Hi' + firstname;
}
}
var aaron = new Person('Aaron', 'Rosario'); // New object from the person object
// The new keyword is an attempt to appease devs comfortable in other classical languages
class InformalPerson extends Person { // sets the Prototype __proto__
constructor(firstname, lastname) {
super(firstname, lastname);
}
greet() {
return "Yo " + firstname;
}
}
// Syntactic sugar: a different way to type something that doesn't change how it works under the hood
// Classes in ES6 dont change anything about how constructors or prototypes work in JS engine
/* Odds and Ends
Initialization
*/
var people = [
{
// the 'Aaron' object
firstname: 'Aaron',
lastname: 'Rosario',
addresses: [
'111 Main St',
'222 Third St.'
]
},
{
// the 'Jane' object
firstname: 'Jane',
lastname: 'Doe',
addresses: [
'333 Main St.',
'444 Fifth St.'
]
},
greet: function() {
return 'Hello!';
}
];
// typeof, instanceof and figuring out what something is
var a = 3;
console.log(typeof a); // number
var b = 'hello';
console.log(typeof b); // string
var c = {}; // object
var d = [];
console.log(typeof d); // object... weird.
console.log(Object.prototype.toString.call(d)); // [object Array], call = invoke this function but tell it what the this should point to
function Person(name) {
this.name = name;
}
console.log(typeof e);
console.log(e instanceof Person); // true, go down the prototype chain to see if its an instance of Person
console.log(typeof undefined); // undefined
console.log(typeof null); // null object, bug thats been around too long to fix
var z = function() { };
console.log(typeof z); // function
/* Strict Mode */
"use strict"; // must be top of the file or top of a function
var person;
persom = {};
console.log(persom); // Uncaught ReferenceError: persom is not defined
/* Let's build a framework/library
Requirements (what should the software do)
Name: greetr
When given a first name, last name and optional language, it generates formal and informal greetings
Support English and Spanish languages
Reusable library/framework.
Easy to type 'G$()'
Support jQuery
*/
(function(global, $) {
var Greetr = function(firstName, lastName, language) {
return new Greetr.init(firstName, lastName, language);
}
var supportedLangs = ['en', 'es'];
var greetings = {
en: 'Hello',
es: 'Hola'
};
var formalGreetings = {
en: 'Greetings',
es: 'Saludos'
};
var logMessages = {
en: 'Logged in',
es: 'Inicio sesion'
}
Greetr.prototype = {
fullName: function() {
return this.firstName + ' ' + this.lastName;
},
validate: function() {
if (supportedLangs.indexOf(this.language) === -1) {
throw "Invalid language";
}
},
greeting: function() {
return greetings[this.language] + ' ' + this.firstName + '!';
},
formalGreeting: function() {
return formalGreetings[this.language] + ', ' + this.fullName();
},
greet: function(formal) {
var msg;
if (formal) {
msg = this.formalGreeting();
}
else {
msg = this.greeting();
}
if (console) {
console.log(msg);
}
return this;
},
log: function() {
if (console) {
console.log(logMessages[this.language] + ': ' + this.fullName());
}
return this;
},
setLang: function(lang) {
this.language = lang;
this.validate();
return this;
},
HTMLGreeting: function(selector, formal) {
if (!$) {
throw 'jQuery not loaded';
}
if (!selector) {
throw 'Missing jQuery selector';
}
var msg;
if (formal) {
msg = this.formalGreeting();
}
else {
msg = this.greeting();
}
$(selector).html(msg);
return this;
}
};
Greetr.init = function(firstName, lastName, language) {
var self = this;
self.firstName = firstName || '';
self.lastName = lastName || '';
self.language = language || 'en';
self.validate();
}
Greetr.init.prototype = Greetr.prototype;
global.Greetr = global.G$ = Greetr;
}(window, jQuery));