Open Holayn opened 4 years ago
undefined
is a special value that indicates that the variable hasn't been set. The JS engine is saying the variable isn't set in memory.
undefined
takes up memory space, as it is a special type of value.
null
is a lack of existence - this variable has no value.
Number
and String
this
refers to global execution contextthis
refers to that objectvar c = {
log: function() {
console.log(this); // c
},
log2: function() {
var d = function() { console.log(this) }
d(); // context where c.log2() is called
},
log3: function() {
var self = this;
console.log(self);
var d = function() { console.log(self) }
d();
}
}
They don't create new scope
if (true) {
// this if statement doesn't create a new scope
// so varInIf is available in the global scope
var varInIf = 'inside an if statement';
}
console.log(varInIf); // logs 'inside an if statement'
let
is block scoped so won't run into this problem
var
is only scoped to functions
resource: https://javascriptweblog.wordpress.com/2010/12/07/namespacing-in-javascript/
module pattern:
var myApp = (function() {
var id= 0;
return {
next: function() {
return id++;
},
reset: function() {
id = 0;
}
};
})();
The logic is shielded from the global scope by a function wrapper (usually self-invoking) which returns an object representing the module’s public interface.
By immediately invoking the function and assigning the result to a namespace variable, we lock up the module’s API in the namespace. Additionally any variables not included in the return value will remain forever private, visible only to the public functions that reference them.
other less ideal approaches:
direct assignment:
var myApp = {}
myApp.id = 0;
myApp.next = function() {
return myApp.id++;
}
myApp.reset = function() {
myApp.id = 0;
}
Object Literal Notation:
var myApp = {
id: 0,
next: function() {
return this.id++;
},
reset: function() {
this.id = 0;
}
}
a new execution context is created and put onto the execution stack. They have their own this
context
even when an execution context is removed, its variable environment / memory space remains (until garbage collections removes it)
execution context contains information about where the function was called from (the call-stack), how the function was invoked, what parameters were passed, etc. One of the properties of this record is the this
reference which will be used for the duration of that function's execution.
this
is a binding made for each function invocation, based entirely on its call-site (how the function is called) - what object is calling it!
Finding the call-site is generally: "go locate where a function is called from", but it's not always that easy, as certain coding patterns can obscure the true call-site. What's important is to think about the call-stack (the stack of functions that have been called to get us to the current moment in execution). The call-site we care about is in the invocation before the currently executing function.
resources: link
`function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
obj.foo(); // 2
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // function reference/alias!
var a = "oops, global"; // `a` also property on global object
bar(); // "oops, global"
function foo() {
console.log( this.a );
}
function doFoo(fn) {
// `fn` is just another reference to `foo`
fn(); // <-- call-site!
}
var obj = {
a: 2,
foo: foo
};
var a = "oops, global"; // `a` also property on global object
doFoo( obj.foo ); // "oops, global"
call()
, apply()
, bind()
this
context when the execution context is created for the functionFunctions will always have access to the variables it's supposed to have access to
You will have closures whenever having function declarations. "Closing" over variables so that have access to them when calling the function.
Useful for:
JS has prototypal inheritance involving a prototype chain
__proto__
that points to the prototype of that object (i.e. the parent)__proto__
- if not there, check its __proto__
, etc
(read more about object links here)Everything is an object (except primitives) and they all have a prototype (except the ultimate JS base Object
which is the bottom of the proto-chain.
toString()
for exampleinstanceof
goes up the __proto__
chain to see if in it
This is different from the scope chain!
Former is property of class constructor, latter is property of class instance
__proto__
is an object in every class instance that points to the prototype it was created from.
iPhone.prototype
provides a blueprint for building an iPhone,newPhone.__proto__
affirms that the iPhone has indeed been built according to that specific blueprint
Constructors have a special property whose unfortunate name is prototype
. The value of the property becomes the prototype of the instances created by that constructor.
function Foo() {}
console.log(Object.getPrototypeOf(new Foo()) === Foo.prototype); // true
var obj = new MyConstructorFunction();
obj.__proto__ === MyConstructorFunction.prototype // true
Note: __proto__
is a recent addition to the language (ES6). [[Prototype]]
is what's referenced in the spec.
Object.defineProperty( Object.prototype, "__proto__", {
get: function() {
return Object.getPrototypeOf( this );
},
set: function(o) {
// setPrototypeOf(..) as of ES6
Object.setPrototypeOf( this, o );
return o;
}
} );
Side notes:
Object.create()
to reliably create linksUse the new
keyword/operator when invoking a function to construct an object. Known as a "function constructor", a replacement for classes
new
creates an empty object, invokes the function, and the execution context for that function points this
to the empty object
function test() {
this.hey = 'hi'
}
const foo = new test()
.prototype()
When object is created by a "function constructor", its prototype chain (__proto__
) points to the function's prototype property. Setting properties on the "function constructor"'s prototype will make them available to the objects created from that function constructor, since the newly created objects prototype chain points to the function's prototype property
Object.create()
Creates a new empty object, with its __proto__
pointing to whatever object that was passed into create()
(vs normally where __proto__
points to the function constructor's prototype)
var person = {
name: 'kai'
}
var wendy = Object.create(person);
wendy.name = 'wendy';
proto
extends
keywordclass B extends A
- prototype of class B points to A
strict
modeTells the parser to be stricter, clean errors instead of hiding them
Changes this
behavior inside of functions - see here
a whole host of other things!
Makes it impossible to accidentally create global variables. Makes assignments which would otherwise silently fail to throw an exception. Makes attempts to delete undeletable properties throw (where before the attempt would simply have no effect). Requires that function parameter names be unique. this is undefined in the global context. It catches some common coding bloopers, throwing exceptions. It disables features that are confusing or poorly thought out.
The execution context has two phases: the creation phase and the execution phase.
Creation phase
Execution phase
Example case of seeing this happening:
var text = 'outside';
function logIt(){
console.log(text);
var text = 'inside';
};
logIt(); // logs undefined since variable DECLARATIONS are hoisted
// really looks like this:
var text;
console.log(text);
text = 'inside'
Function declarations are hoisted over variable declarations but not over variable assignments
Readings: link
Variable assignment over function declaration
var double = 22;
function double(num) {
return (num*2);
}
console.log(typeof double); // Output: number
Function declarations over variable declarations
var double;
function double(num) {
return (num*2);
}
console.log(typeof double); // Output: function
BASICALLY
let
to avoid these weird cases - you can't reference let
declared vars before they're declaredMethods can be assigned to the class function itself, not to its prototype
.
This is the same thing as assigning things to a class itself.
Used to implement functions that belong to the class but not to any particular object of that class.
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static compare(articleA, articleB) {
return articleA.date - articleB.date;
}
}
They don't have their own this
pointing to their execution context
Uses lexical scope to resolve this
- i.e. the this
of wherever the function was defined. "The value of this
inside an arrow function is always inherited from the enclosing scope."
Bound to the same this
context of where it was created.
Useful for accessing the this
of the current env, i.e. in callbacks (map
).
Practically equivalent:
function () {
return this;
}.bind(this);
() => {
return this;
};
The this
binding can never be overriden!
Related to functional programming.
Not mutating objects, instead create new copies of them. Less complicated programs (since don't have to worry about objects mutating)
Naive implementations of immutable data structures and its operations can result in extremely poor performance because new objects are created each time. It is recommended to use libraries for efficient immutable data structures and operations that leverage on structural sharing.
Reads:
TIL: https://stackoverflow.com/questions/7744611/pass-variables-by-reference-in-javascript another reason why modifying params is a bad idea