Presenter: Yauheni Pozdnyakov
t.me/youhy
It is not compiled in advance, as C++, nor are the results of compilation portable among various distributed systems as Java or C# do
Compilation steps:
Any snippet of JavaScript has to be compiled before (usually right before!) it's executed
var a = 2;
Compiler:
Engine:
Side??? What side???
Of an assignment operation!
RHS look-up - a look-up of the value of some variable, like "go get the value of.." (who's the source of the assignment )
LHS look-up is trying to find the variable container itself, so that it can assign (who's the target of the assignment)
var a = 2;//LHS
function foo(a) {
console.log( a ); // 2
}
foo( 2 );
function foo(a) {
var b = a;
return a + b;
}
var c = foo( 2 );
3 - LHS look-ups
4 - RHS look-ups
LHS:
c =...
a = 2
b = ..
RHS:
foo(2..
= a;
a + ..
.. + b
function foo(a) {
console.log( a + b );
}
var b = 2;
foo( 2 ); // 4
RHS reference for b cannot be resolved inside the function foo!
What happens?
Where do ReferenceError and TypeError come from?
function foo(a) {
console.log( a + b );
b = a;
}
foo( 2 );
function foo(a) {
b = a;
console.log( a + b );
}
foo( 2 );
ReferenceError: b is not defined
4
RHS lookup - succeeded! But you're trying to perform something impossible - TypeError
lexical scope is scope that is defined at lexing time
function foo(a) {
var b = 2;
// some code
function bar() {
// ...
}
// some more code
var c = 3;
}
bar(); // fails
console.log( a, b, c ); // also fails
var a = 2;
function foo() {
var a = 3;
console.log( a );
}
foo();
console.log( a );
// 2
var a = 2;
(function IIFE(){
var a = 3;
console.log( a );
})();
console.log( a );
// 3
// 2
var a = 2;
(function IIFE( global ){
var a = 3;
console.log( a );
console.log( global.a );
})( window );
console.log( a );
// 3
// 2
// 2
IFFE: Immediately Invoked Function Expression
a = 2;
var a;
console.log( a );
//2
var a;
a = 2;
console.log( a );
console.log( a );
var a = 2;
//undefined
var a; //undefined
console.log( a );
a = 2;
foo();
function foo() {
console.log( a ); // undefined
var a = 2;
}
foo(); // not ReferenceError, but TypeError!
var foo = function bar() {
// ...
};
var foo; //undefined
foo(); // not ReferenceError, but TypeError!
foo = function bar() {
// ...
};
foo(); // 1
var foo;
function foo() {
console.log( 1 );
}
foo = function() {
console.log( 2 );
};
function foo() {
console.log( 1 );
}
foo(); // 1
foo = function() {
console.log( 2 );
};
foo(); // 3
function foo() {
console.log( 1 );
}
var foo = function() {
console.log( 2 );
};
function foo() {
console.log( 3 );
}
function foo() {
var a = 2;
function bar() {
console.log( a );
}
return bar;
}
var baz = foo();
baz();
// 2
bar() still has a reference to that scope, and that reference is called closure.
Closure lets the function continue to access the lexical scope it was defined in at author-time.
var fn;
function foo() {
var a = 2;
function baz() {
console.log( a );
}
fn = baz; // assign `baz` to global variable
}
function bar() {
fn(); // look ma, I saw closure!
}
foo();
bar(); // 2
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( " ! " ) );
}
return {
doSomething: doSomething,
doAnother: doAnother
};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
This is contextual based on the conditions of the function's invocation
Depends on the manner in which the function is called
When a function is invoked, an activation record (execution context) is created. It contains information about where the function was called from (the call-stack), how the function was invoked, what parameters were passed, etc.
function baz() {
console.log( "baz" );
bar();
}
function bar() {
console.log( "bar" );
foo();
}
function foo() {
console.log( "foo" );
}
baz();
// call-stack is: `baz`
// so, our call-site is in the global scope
// <-- call-site for `bar`
// call-stack is: `baz` -> `bar`
// so, our call-site is in `baz`
// <-- call-site for `foo`
// call-stack is: `baz` -> `bar` -> `foo`
// so, our call-site is in `bar`
// <-- call-site for `baz`
function foo() {
console.log( this.a );
}
var a = 2;
foo(); // 2
default binding for this applies to the function call
function foo() {
console.log( this.a );
}
var obj = {
a: 2,
foo: foo
};
var bar = obj.foo; // function reference
var a = "oops, global"; // `a` also property on global object
bar(); // "oops, global"
un-decorated call, the default binding applies
bind(..), call(..) and apply(..) methods
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call( obj ); // 2
From point of this binding, call(..) and apply(..) are identical. They do behave differently with their additional parameters.
function foo(something) {
console.log( this.a, something );
return this.a + something;
}
var obj = {
a: 2
};
var bar = foo.bind( obj );
var b = bar( 3 ); // 2 3
console.log( b ); // 5
bind(..) returns a new function that is hard-coded to call the original function with the this context set as you specified.
Is the function called with new (new binding)? If so, this is the newly constructed object.
var bar = new foo()
Is the function called with call or apply (explicit binding), even hidden inside a bind hard binding? If so, this is the explicitly specified object.
var bar = foo.call( obj2 )
Is the function called with a context (implicit binding), otherwise known as an owning or containing object? If so, this is that context object.
var bar = obj1.foo()
Otherwise, default the this (default binding). If in strict mode, pick undefined, otherwise pick the globalobject.
var bar = foo()
Called with new? Use the newly constructed object.
Called with call or apply (or bind)? Use the specified object.
Called with a context object owning the call? Use that context object.
Default: undefined in strict mode, global object otherwise.
Determining the this binding for an executing function requires finding the direct call-site of that function.