Javascript internals

44
© Copyright SELA Software & Education Labs Ltd. | 14-18 Baruch Hirsch St Bnei Brak, 51202 Israel | www.selagroup.com SELA DEVELOPER PRACTICE JUNE 19-23, 2016 Nir Noy Consultant, Web, Sela @noynir Javascript Internals

Transcript of Javascript internals

Page 1: Javascript internals

© Copyright SELA Software & Education Labs Ltd. | 14-18 Baruch Hirsch St Bnei Brak, 51202 Israel | www.selagroup.com

SELA DEVELOPER PRACTICEJUNE 19-23, 2016

Nir NoyConsultant, Web, Sela@noynir

Javascript Internals

Page 2: Javascript internals

JavaScript InternalsJavaScript has Evolved into more then just a small scripting language that run inside our browsers.

It’s The Engine behind large scale client side applications, mobile applications, desktop and web servers.

The dynamic nature of JavaScript makes us believe that almost everything is allowed and there are no restrictions.

Page 3: Javascript internals

JavaScript InternalsIt’s important to understand what’s going on “behind the scenes” helps us:

Write better and efficient code.Profile our application.Debug our application.

Page 4: Javascript internals

Scope

Page 5: Javascript internals

ScopeWhat is Scope?

Stores all the declared identifiers (variables), and enforces a strict set of rules as to how these are accessible to the executing code.

Javascript is Scope is lexicalLexical scope means that scope of a variable is defined by it’s location within the source code.

In the ECMAScript spec The Scope is referred to asLexicalEnvironment

Page 6: Javascript internals

Function And Global ScopeUntil Now JavaScript had 2 scope levels:

Function scope – Every function has it’s own scope, all declared variablesand functions inside the functions body, are associated with it.

Global scope – All vairables and functions associated with it can be accesed from anywhere, and all bindings are saved as the global object’s properties.

ECMAScript 2015 Introduced Block scope and module scope.

Page 7: Javascript internals

add Function[[Environment]]

Lexical Environment

[[Strict]] Boolean[[call]] Method

add Lexical EnvironmentEnvironment Record

{ key:value}outer Lexical

Environment

Global Environment

Environment Record:{ this:window, window:(object), document(object), …}

outer null

function add(num1, num2){ var sum = num1 + num2 ; return sum;}add(1,2);

Page 8: Javascript internals

Execution ContextAn Execution context is used to track the runtime evaluation of code.

At any point in time There is only one execution context that is actually executing code, this is called The running execution context

The Execution Contexts are pushed onto a stack, where the running execution context is always on top.

Page 9: Javascript internals

EC Lexical Environment

Environment Record:{ num1:1, num2:2, sum:undefined}

outer

Global Environment

Environment Record:{ this:window, window:(object), document(object), …}outer null

function add(num1, num2){ var sum = num1 + num2; return sum;}add(1,2);

add Execution Context[[LexicalEnvironment]][[VariableEnvironment]][[Function]] add

Page 10: Javascript internals

Identifier ResolutionOn code execution Variables and functions are looked up first in the Execution Context’s [[LexicalEnvironment]] and if not found are recursively looked up in the Lexical environment’s outer scope until reaching the global scope.

This is also referred to as the Scope Chain.

If they are not found in global scope there are several use cases:

Read value – Reference ErrorWrite value – automatically creates the variable and returns it’s reference.Write value with strict mode – Reference Error.

Page 11: Javascript internals

Identifier Resolution optimizationSaving a local copy of a parent scope variable can reduce the length of the scope chain on every lookup.

function initUI(){ var bd = document.body, links = document.getElementsByTagName("a"), i= 0, len = links.length; while(i < len){ update(links[i++]); } document.getElementById("go-btn").onclick = function(){ start(); }; bd.className = "active"; }

function initUI(){ var doc = document, bd = doc.body, links = doc.getElementsByTagName("a"), i= 0, len = links.length; while(i < len){ update(links[i++]); } doc.getElementById("go-btn").onclick =function(){ start(); }; bd.className = "active";}

Page 12: Javascript internals

EC Lexical Environment

Environment Record:

{ num1:1, num2:2, sum:3}outer

Global Environment

Environment Record:{ this:window, window:(object), document(objext), …}

outer null

function add(num1, num2){ var sum = num1 + num2; return sum;}add(1,2);

Global Execution Context[[LexicalEnvironment]][[VariableEnvironment]][[Function]] nul

l

add Execution Context[[LexicalEnvironment]][[VariableEnvironment]][[Function]] add

Page 13: Javascript internals

Closures

Page 14: Javascript internals

What is Closure?Closure is when a function is able to remember and access its lexical scope even when that function is executing outside its lexical scope.function display(name){ function greet(){ alert('my name is '+name) } return greet;}var notify=display(‘nir’);notify();

Page 15: Javascript internals

display Execution Context

[[Environment]]

display EC Lexical Environment

Environment Record:{ name:’nir’, greet:Function}outer

Global EnvironmentEnvironment Record:

{ notify:undefined, …}outer null

function display(name){ function greet(){ alert('my name is '+name) } return greet;}var notify=display(‘nir’);notify();

greet Lexical Environment

Environment Record:{}

outer

Page 16: Javascript internals

display Execution Context

[[Environment]]

display EC Lexical Environment

Environment Record:{ name:’nir’, greet:Function}outer

Global EnvironmentEnvironment Record:

{ notify:Function …}outer null

function display(name){ function greet(){ alert('my name is '+name) } return greet;}var notify=display(‘nir’);notify();

greet Lexical Environment

Environment Record:{}

outer

Global Execution Context

[[Environment]]

Page 17: Javascript internals

ClosuresClosures are essential for JavaScript async nature, as they allow us to save the state of the environment they were created in.Saving the Closure’s state comes with some performance overhead

Lookups – closures adds at least another level in the scope chain.Memory – The Execution Context of the containing Environment is kept in memory because the closure’s Environment’s outer property is referencing it.

This can Lead to increased memory consumption and memory leaks.

Page 18: Javascript internals

Closures TipsAn Object property lookups are faster then variables lookups on the scope chain.

function display(name){ function greet(){ alert('my name is '+name) } return greet;}var notify=display(‘nir’);notify();

function Notifier(_name) { this.name = _name;};Notifier.prototype.notify=function(){ alert('my name is '+this.name);}function display(name){ return new Notifier(name);}var notifier=display(‘nir’); notifier.notify();

Page 19: Javascript internals

Closures TipsModern Browsers JavaScript engines create the closure only when necessary.

Page 20: Javascript internals

Closures Tips - For LoopsRemember that the closure’s outer reference is live, every change you make after the closure is created would reflect in the closure’s scope.

var funcs = [];for (var i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value: " + i); };}for (var j = 0; j < 3; j++) { funcs[j]();}

Funcs[0] Closure

Environment Record

outer

Funcs[1] Closure

Environment Record

outer

Funcs[2] Closure

Environment Record

outer

EC Lexical EnvironmentEnvironment Record

{ i:3,}

Page 21: Javascript internals

Block ScopeBlock scoped variables are are accessible only inside the block of code they are defined in.

You can create Block scope to control the life time of the variables you define.

With Closures you can use Block scope to save the lexical state at the time the closure was created.

Page 22: Javascript internals

Block ScopeUsing an IIFE - immediately invoked Function expression.

var funcs = [];for (var i = 0; i < 3; i++) { (function(inx){

funcs[inx] = function() { console.log("My value: " + inx); };

}(i));}for (var j = 0; j < 3; j++) { funcs[j]();}

Page 23: Javascript internals

ECMAScript 2015 Block ScopeECMAScript 2015 has introduced true Block scope.

In ECMAScript 2015 when execution enters a block:A new Lexical Environment is created with it’s outer environment referencing the running Execution Context’s Lexical Environment.The Newly created lexical Environment is replacing is set as the running execution context’s lexical Environment.

ECMAScript 2015 has also introduced the “let” keyword that allows declaring block scoped variables.

Page 24: Javascript internals

var funcs = [];for (var i = 0; i < 3; i++) { let inx=i funcs[inx] = function() { console.log("My value: " + inx); }; }for (var j = 0; j < 3; j++) { funcs[j]();}

Running Execution Conext

[[Lexical Environment]][[Variable Environment]]

Initial Lexical Environment

Environment Record

{ i:undefined, j:undefined}outer null

Iteration 1 Lexical Environment

Environment Record

{ inx:0}outer

Iteration 1 Closure

Environment Record {}

outer

Iteration 2 Lexical Environment

Environment Record

{ inx:1}outer

Iteration 2 Closure

Environment Record {}

outer

Iteration 3 Closure

Environment Record {}

outer

Iteration 3 Lexical Environment

Environment Record

{ inx:2}outer

var funcs = [];for (let i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value: " + i); };

}for (var j = 0; j < 3; j++) { funcs[j]();}

Page 25: Javascript internals

Scope & Closures SummaryOptimize your variable lookup by reducing the scope chain.

Use strict mode to avoid unexpected behavior.When working on existing code, don’t use strict mode globally as it may break your code.

Try to avoid closures as they keep the lexical scope on memory and prevent it from being GC’ed.

Page 26: Javascript internals

Objects & Prototypes

Page 27: Javascript internals

Javascript is Object OrientedAs opposed to other “Object Oriented” languages Javascript is perhaps The Only one that justifies this label.

In Javascript an object can be created directly without any Class definition.

There are no Classes that defines the object’s behavior.

Page 28: Javascript internals

Javascript ObjectsObjects in Javascript have an internal [[prototype]] property.

The [[prototype]] property is simply a reference to another object.

Almost all objects are given a non-null value to this property at the time of their creation.

Page 29: Javascript internals

Property lookupsLookup is done via the [[Get]] internal operation.

When we reference a property such as obj.prop, the [[Get]] first step is to check if the object itself has a property prop on it.

If prop is not present on the object, the [[Get]] proceeds to follow the [[Prototype]] link of the object.

This process continues until either a matching property name is found, or the [[Prototype]] chain ends. If no matching property is ever found by the end of the chain, the return result from the [[Get]] operation is undefined.

Page 30: Javascript internals

PrototypesEvery Function Object in Javascript has property named prototype which

By default this object is an empty object that it’s [[Prototype]] references the Javascript Object type prototype.

Javascript Object type prototype contains method such as toString(), hasOwnProperty() etc…

Page 31: Javascript internals

ConstructorsWhen an object is created using the new keyword the obj [[Prototype]] is linked to the object MyObject.prototype is pointing to.

function MyObject(id){ this.id=id;};

var obj = new MyObject(id);Object.getPrototypeOf(obj) === MyObject.prototype; //true

Page 32: Javascript internals

Classes VS. Prototypes

MyObject Class

Obj3Obj2Obj1

MyObject Prototype

Obj3Obj2Obj1

Page 33: Javascript internals

Prototypical Inheritancefunction baseController(_name) { this.name = _name;}baseController.prototype.onError = function { //some error handling here..};baseController.prototype.onSuccess = function{ //some success handling here..};function loginController(_name) { baseController.call(this,_name);}loginController.prototype = Object.create(baseController.prototype);loginController.prototype.login = function { //Some login logic here...};

function authController(_name) { loginController.call(this,_name)}authController.prototype = Object.create(loginController.prototype);authController.prototype.auth = function { if (loginController.prototype.login.call(this)) { //Some auth logic here... }};

var loginCtrl = new loginController('login');

var authCtrl = new authController('auth');

Page 34: Javascript internals

Prototypical Inheritance modelbaseController

constructor Function baseController

Prototype

Login Controller constructor Function

LoginCtrlAuthCtrl

AuthController constructor Function

LoginController Prototype

AuthController Prototype

.prototype

[[Prototype]]

[[Prototype]]

[[Prototype]][[Prototype]]

.prototype

.prototype

Page 35: Javascript internals

ECMAScript 2015 ClassesUntil ECMAScript 2015 the biggest issue with implementing classical inheritance was the lack of ability to reference the base class.

We would have to resolve into directly calling the prototype methods of the base class.

ES2015 Classes are just “syntactic suger” to the exsting prototype mechanism.

Page 36: Javascript internals

class baseController { constuctor(_name){ this.name=_name; } onError(err){ //some error handling here.. } onSuccess(){ //some success handling here.. }}class loginController extends baseController { constuctor(name){ super(name); } login(){ //Some login logic here... }}

class authController extends loginController { constuctor(name){ super(name); }

auth(){ if(super.login()){ //Some auth logic here... } }}

var loginController=new loginController('login');

var authController = new authController('auth');

Page 37: Javascript internals

ECMAScript 2015 Function ObjectFunction {

…[[Environment]] : LexicalEnvironment, //This is The Scope[[ECMAScriptCode]] : ParseNode,[[Strict]] : Boolean,[[FunctionKind]]:"normal","classConstructor”,"generator”,[[ConstructorKind]]:”base”,”derived”,[[HomeObject]]: Object …[[call]](thisArgument, argumentsList) : Method…

}

Page 38: Javascript internals

var baseController={ name:null, onError(err){ //some Error Handling here... }, onSuccess(){ //some Success Handling here... }}var loginController={ name:null, login(){ //Some login logic here... }};

Object.setPrototypeOf(loginController,baseController);

loginController.name='login';var authController= { name:null, auth:function(){ if(this.login()){ //Some auth logic here... } }};

Object.setPrototypeOf(authController,loginController);

authController.name='auth';

Object Composition

Page 39: Javascript internals

Prototypical Inheritance modelbaseController

constructor Function baseController

Prototype

Login Controller constructor Function

LoginCtrlAuthCtrl

AuthController constructor Function

LoginController Prototype

AuthController Prototype

.prototype

[[Prototype]]

[[Prototype]]

[[Prototype]][[Prototype]]

.prototype

.prototype

Page 40: Javascript internals

Object Composition Model

loginController

AuthController

baseController

[[Prototype]]

[[Prototype]]

Page 41: Javascript internals

Object CompositionObject.setPrototypeOf() directly sets the [[Prototype]] of the object.

Using Object.create() and Object.setPrototypeOf() enables us to directly create and manipulate the prototype chain.

Object Composition produces a simpler object model, try to favor it when ever possible.

Page 42: Javascript internals

Questions

Page 43: Javascript internals

Additional ResourcesECMAScript - http://www.ecma-international.org/ecma-262/6.0/ Blogs to follow

ECMA 262 in Detail - http://dmitrysoshnikov.com/Dr. Axel Rauschmayer - http://www.2ality.com/Nicholas Zakas - https://www.nczonline.net/

Books:You Don’t Know JS - https://github.com/getify/You-Dont-Know-JS

Page 44: Javascript internals

Thank youNir Noy | @noynir | [email protected]