Javascript internals

Post on 13-Feb-2017

242 views 0 download

Transcript of 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

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.

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.

Scope

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

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.

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);

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.

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

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.

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";}

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

Closures

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();

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

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]]

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.

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();

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

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,}

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.

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]();}

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.

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]();}

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.

Objects & Prototypes

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.

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.

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.

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…

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

Classes VS. Prototypes

MyObject Class

Obj3Obj2Obj1

MyObject Prototype

Obj3Obj2Obj1

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');

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

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.

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');

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…

}

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

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

Object Composition Model

loginController

AuthController

baseController

[[Prototype]]

[[Prototype]]

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.

Questions

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

Thank youNir Noy | @noynir | nirn@sela.co.il