SlideShare a Scribd company logo
ES6 metaprogramming unleashed
are we ready?
Metaprogramming is powerful and
fun, but remember:
"With great power comes
great responsibility"
about me
Javier Arias Losada, senior software engineer at Telefonica.
Currently working at EyeOS, helping to create an VDI (Virtual
Desktop Infrastructure) platform.
@javier_arilos
http://about.me/javier.arilos
metaprogramming
“The key property of metaprograms is that
manipulate other programs or program
representations.”
- Gregor Kiczales
metaprogramming levels
Meta level:
program manipulating other program
Base level:
program manipulated
metaprogramming samples
Compiler/transpilers: gcc, coffeescript…
Macro languages (eg. C preprocessor)
Using "eval" to execute a string as code
Database scaffolding/ORM: mongoose, …
IDEs: Webstorm, …
reflective metaprogramming
A program that modifies itself -
Reflective Metaprogramming in JS-
This is the subject of the talk!
MMM… very
interesting … but
when are we
going to JS?
Introspection
Introspection: read the structure of a program.
Object.keys()
var obj = { //Base level
name: 'Santa',
hello: function() {
console.log('Hello', this.name,'!');
}
};
Object.keys(obj).forEach(function(prop) {
console.log(prop); //Meta level
});
Self-modification
Self-modification: change program structure.
function renameProperty(obj, oldName, newName) {
obj[newName] = obj[oldName];
delete obj[oldName];
}
renameProperty(obj, 'hello', 'greet');
Intercession
Intercession: redefine semantics of some language operations.
Object.defineProperty(obj, "roProp", {
value: 101,
writable: false,
enumerable: true});
obj.roProp = 102
obj.roProp //101
‘Cheating’ quine:
(function a(){console.log('('+a+')()')})()
‘Cheating’ quine:
(function a(){console.log('('+a+')()')})()
A real one:
_='"'+";document.write('_=',_[17],_[0],_[17],'+',_,_)";document.write('_=',_[17],_
[0],_[17],'+',_,_)
metaprogramming fun: Quines
Quine: a program that prints a copy of its own
source code (no input allowed)
Caffeine Facts:
A cup of coffee works just 10
minutes after you drink it.
It takes 45 minutes for 99% of
caffeine to be absorbed.
Please… awake people
around you!!
JS metaprogramming up to ES5
THE GOOD:
object metaprogramming API
THE UGLY:
function metaprogramming
THE BAD:
eval
The Good: Object metapgrming API
● modify property access:
○ getters & setters
○ property descriptors
● Object mutability:
preventExtensions,
seal, freeze
Sample: Test Spy
Test Spy myFunction
[1] myFunction = spy (myFunction)
[5] assert eg. calledOnce
[2] myFunction(1, ‘a’)
Test spy is a function that records calls to a spied function
- SinonJS
[3] store call [4] call
// we define a very interesting function
function sayHi(name){ console.log('Hi '+name+'!') }
// now we Spy on sayHi function.
sayHi = functionSpy(sayHi);
console.log('calledOnce?', sayHi.once); // false. Note that ‘once’ looks like a property!!
sayHi('Gregor'); // calling our Function!!
console.log('calledOnce?', sayHi.once); // true
Sample: Test Spy
accessor properties sample - Spy
function functionSpy(func){
var proxyFunc = function () { //intercept and count calls to func
proxyFunc._callCount += 1;
return func.apply(null, arguments);
};
Object.defineProperty(proxyFunc, "_callCount", {value: 0, writable: true});
Object.defineProperty(proxyFunc, "once", {get: function(){return this._callCount==1});
return proxyFunc;
}
The Bad: eval
avoid using eval in the browser for input from the user or your
remote servers (XSS and man-in-the-middle)
“is sometimes necessary, but in most cases it
indicates the presence of extremely bad coding.”
- Douglas Crockford
The Ugly: func metaprogramming
● Function constructor
● Function reflection:
○ Function.prototype.length
○ Ugly hacks
var remainder = new Function('a', 'b', 'return a % b;');
remainder(5, 2); // 1
function constructor
Seems similar to eval but…
function constructor vs eval
function functionCreate(aParam) { //Func consctructor cannot access to the closure
var funcAccessToClosure = Function('a', 'b', 'return a + b + aParam');
return funcAccessToClosure(1, 2);
}
functionCreate(3); //ReferenceError: aParam is not defined
function functionInEval(aParam) {//has access to the closure
eval("function funcAccessToClosure(a, b){return a + b + aParam}");
return funcAccessToClosure(1, 2);
}
functionInEval(3); // 6
var aParam = 62; //Now, define aParam.
functionCreate(3); // 65
functionInEval(3); // 6
function constructor vs eval
function functionCreate(aParam) { //Func consctructor cannot access to the closure
var funcAccessToClosure = Function('a', 'b', 'return a + b + aParam');
return funcAccessToClosure(1, 2);
}
functionCreate(3); //ReferenceError: aParam is not defined
function functionInEval(aParam) {//has access to the closure
eval("function funcAccessToClosure(a, b){return a + b + aParam}");
return funcAccessToClosure(1, 2);
}
functionInEval(3); // 6
var aParam = 62; //Now, define aParam.
functionCreate(3); // 65
functionInEval(3); // 6
function reflection - length
Function.length returns the number of parameters
of a function.
Usage example: Express checking middlewares signature
function parameters reflection
We want to get informaton about function
parameters.
Parameters belong to function signature.
Arguments are passed to a function call.
function parameters reflection
Is it a bad joke?
Function.toString() + RegExp
to the rescue!
How do we do that in JS?
DI container implementation
Defined in ES5 and ES2015 specs.
getParameters : function(func) { //The regex is from Angular
var FN_PARAMS = /^functions*[^(]*(s*([^)]*))/m;
var funcParams = func.toString().match(FN_PARAMS)[1].split(',');
return funcParams;
}
fucntion parameters reflection
Some libraries with Dependency Injection, such
as Angular.js use this technique:
Eyes still opened?
Coffee must be working!
ES6 and Proxy
The Proxy can define custom behavior for
fundamental operations.
property lookup, assignment, enumeration, function invocation
Proxy concepts
handler:
interceptor. traps per operation.
proxy
&
handler
target
A Proxy wraps a target object.
target:
proxied object.
proxy sample: noSuchPropertyze
var myObj = {
a: 1,
b: 'nice'
};
myObj = noSuchPropertyze(myObj); // We want to INTERCEPT access to properties (get)
myObj.b; // nice
myObj.nonExistingProperty; // Error
function noSuchPropertyze(obj) {
var handler = {
get: function(target, name, receiver){
if(name in target) return target[name];
throw new Error('Not found:' + name);
}
};
return new Proxy(obj, handler);
}
var myObj = noSuchPropertyze({a: 1, b:
'nice'});
myObj.b; // nice
myObj.nonExistingProperty; // Error
proxy sample: noSuchPropertyze
proxy
&
handler
target
myObj[name]
Proxy usages
Why do we need proxies?
virtual objects: persistent or remote objects
emulate the dreaded "host objects"
do fancy things such as DSLs
proxy sample: DRY REST Client
// REST client
let myRestClient = {
getClient: function(id) {
console.log('HTTP GET /server/client/'( id ? '/'+id : ''));
return 200;
},
getBill: function(id) {
console.log('HTTP GET /server/bill/'( id ? '/'+id : ''));
return 200;
},
// (...) DO YOU SEE THE PATTERN?
}
myRestClient.allo = 7;
myRestClient.getClient('kent.beck'); //200 "HTTP GET
/server/client/kent.beck"
myRestClient.allo; // 7;
proxy sample: DRY REST Client
function prepareGetter(resource) {
return function resourceGetter(id) {
console.log('HTTP GET /server/'+resource+( id ? '/'+id : ''));
return 200;
}}
let proto = new Proxy({}, {
get(target, name, receiver) {
if(name.startsWith('get')) {
return prepareGetter(name.slice(3).toLowerCase());}
return target[name];
}});
let myRestClient = Object.create(proto); //Prototype is a Proxy
myRestClient.allo = 7;
myRestClient.getClient('kent.beck'); //200 "HTTP GET /server/client/kent.
beck"
myRestClient.allo; // 7;
DSL with Proxies
to(3).double.pow.get // 36
DSL with Proxies- implementation
// ==== to(3).double.pow.get ===
var to = (function closure() { // closure for containing our context
var functionsProvider = { //Object containing our functions
double: function (n) { return n*2 },
pow: function (n) { return n*n }
};
return function toImplementation(value) { // Magic happens here!
// (...) implementation
return new Proxy(functionsProvider, handler);
}
}());
DSL with Proxies- implementation
// ==== to(3).double.pow.get ===
return function toImplementation(value) {
var pipe = []; //stores functions to be called
var handler =
{ get(target, fnName, receiver) {
if (fnName in target){ //eg. .double : get the function and push it
pipe.push(target[fnName]);
return receiver;} //receiver is our Proxy object: to(3)
if (fnName == "get")
return pipe.reduce(function (val, fn) { return fn(val) }, value);
throw Error('Method: '+ fnName +' not yet supported');
}, set(target, fnName, fn, receiver) {
target[fnName] = fn;} //dynamic declaration of functions
};
return new Proxy(functionsProvider, handler);}}());
DSL with Proxies- implementation
// ==== to(3).double.pow.get ===
return function toImplementation(value) {
var pipe = []; //stores functions to be called
var handler =
{ get(target, fnName, receiver) {
if (fnName in target){ //eg. .double : get the function and push it
pipe.push(target[fnName]);
return receiver;} //receiver is our Proxy object: to(3)
if (fnName == "get")
return pipe.reduce(function (val, fn) { return fn(val) }, value);
throw Error('Method: '+ fnName +' not yet supported');
}, set(target, fnName, fn, receiver) {
target[fnName] = fn;} //dynamic declaration of functions
};
return new Proxy(functionsProvider, handler);}}());
DSL with Proxies- implementation
// ==== to(3).double.pow.get ===
return function toImplementation(value) {
var pipe = []; //stores functions to be called
var handler =
{ get(target, fnName, receiver) {
if (fnName in target){ //eg. .double : get the function and push it
pipe.push(target[fnName]);
return receiver;} //receiver is our Proxy object: to(3)
if (fnName == "get")
return pipe.reduce(function (val, fn) { return fn(val) }, value);
throw Error('Method: '+ fnName +' not yet supported');
}, set(target, fnName, fn, receiver) {
target[fnName] = fn;} //dynamic declaration of functions
};
return new Proxy(functionsProvider, handler);}}());
DSL with Proxies - new methods
to(2).triple.get; //Error: Method: triple not yet supported
to().triple = function(n) {return n*3}; //Add new method: triple
to(2).triple.get; // 6
That’s all folks!
No animals were harmed in the preparation of
this presentation.
references
● Alex Rauschmayer on Proxies: http://www.2ality.com/2014/12/es6-proxies.html
● About quines: http://c2.com/cgi/wiki?QuineProgram
● Kiczales on metaprogramming and AOP: http://www.drdobbs.com/its-not-metaprogramming/184415220
● Brendan Eich. Proxies are awesome: http://www.slideshare.net/BrendanEich/metaprog-5303821
● eval() isn’t evil, just misunderstood: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-
just-misunderstood/
● On DI: http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript
● Express middlewares: http://expressjs.com/guide/using-middleware.html
● Proxies by Daniel Zautner: http://www.dzautner.com/meta-programming-javascript-using-proxies/
Media
● Storm by Kelly Delay: https://flic.kr/p/seaiyf
● The complete explorer: https://www.flickr.com/photos/nlscotland/
● Record Player by Andressa Rodrigues: http://pixabay.com/en/users/AndressaRodrigues-40306/
● Wall by Nicole Köhler: http://pixabay.com/en/users/Nikiko-268709/
● Remote by Solart: https://pixabay.com/en/users/solart-621401/
● Rocket launch by Space-X: https://pixabay.com/en/users/SpaceX-Imagery-885857/
● Coffee by Skeeze: https://pixabay.com/en/users/skeeze-272447/
● Sleeping Monkey by Mhy: https://pixabay.com/en/users/Mhy-333962/
● Funny Monkey by WikiImages: https://pixabay.com/en/users/WikiImages-1897
● Lemur by ddouk: https://pixabay.com/en/users/ddouk-607002/
complete code examples
function sayHi(name){ console.log('Hi '+name+'!') }// we define a very interesting function
sayHi = functionSpy(sayHi);// now we Spy on sayHi function.
console.log('calledOnce?', sayHi.once); // false. Note that ‘once’ looks like a property!!
sayHi('Gregor'); // calling our Function!!
console.log('calledOnce?', sayHi.once); // true
function functionSpy(func){
var proxyFunc = function () { //intercept and count calls to func
proxyFunc._callCount += 1;
return func.apply(null, arguments);
};
Object.defineProperty(proxyFunc, "_callCount", {value: 0, writable: true});
Object.defineProperty(proxyFunc, "once", {get: function(){return this._callCount==1});
return proxyFunc;
}
Test Spy
DI container
● Function reflection (parameters) eg: Dependency Injection
var Injector = {dependencies: {},
add : function(qualifier, obj){
this.dependencies[qualifier] = obj;},
get : function(func){
var obj = Object.create(func.prototype);
func.apply(obj, this.resolveDependencies(func));
return obj;},
resolveDependencies : function(func) {
var args = this.getParameters(func);
var dependencies = [];
for ( var i = 0; i < args.length; i++) {
dependencies.push(this.dependencies[args[i]]);}
return dependencies;},
getParameters : function(func) {//This regex is from require.js
var FN_ARGS = /^functions*[^(]*(s*([^)]*))/m;
var args = func.toString().match(FN_ARGS)[1].split(',');
return args;}};
var aFancyLogger = {
log: function(log){console.log(Date().toString()+" => "+ log);}
};
var ItemController = function(logger){
this.logger = logger;
this.doSomething = function(item){this.logger.log("Item["+item.id+"] handled!");};
};
Injector.add("logger", aFancyLogger); //register logger into DI container
var itemController = Injector.get(ItemController); //get Item controller from DI
itemController.doSomething({id : 5});
DSL with Proxies
var to = (function closure() {
var functionsProvider = {
double: function (n) { return n*2 },
pow: function (n) { return n*n }
};
return function toImplementation(value) {
var pipe = [];
var handler =
{
get(target, fnName, receiver) {
if (fnName == "get")
return pipe.reduce(function (val, fn) { return fn(val) }, value);
if (fnName in target) {
pipe.push(target[fnName]);
return receiver;}
throw Error('Method: '+ fnName +' not yet supported');
},
set(target, fnName, fn, receiver) {
target[fnName] = fn;} //dynamic declaration of functions
};
return new Proxy(functionsProvider, handler);}}());
console.log('to(3).double.pow.get::',to(3).double.pow.get); // 36
console.log('to(2).triple::', to(2).triple.get); //Error: Method: triple not yet supported
to().triple = function(n) {return n*3};
console.log('to(2).triple::',to(2).triple.get);

More Related Content

PDF
OSCON - ES6 metaprogramming unleashed
Javier Arias Losada
 
ODP
The promise of asynchronous PHP
Wim Godden
 
PDF
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
Matthias Noback
 
PPT
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf Conference
 
PPT
Symfony2 Service Container: Inject me, my friend
Kirill Chebunin
 
PPTX
Php string function
Ravi Bhadauria
 
PDF
Modern Objective-C @ Pragma Night
Giuseppe Arici
 
PPTX
Zephir - A Wind of Change for writing PHP extensions
Mark Baker
 
OSCON - ES6 metaprogramming unleashed
Javier Arias Losada
 
The promise of asynchronous PHP
Wim Godden
 
High Quality Symfony Bundles tutorial - Dutch PHP Conference 2014
Matthias Noback
 
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
ZFConf Conference
 
Symfony2 Service Container: Inject me, my friend
Kirill Chebunin
 
Php string function
Ravi Bhadauria
 
Modern Objective-C @ Pragma Night
Giuseppe Arici
 
Zephir - A Wind of Change for writing PHP extensions
Mark Baker
 

What's hot (20)

PDF
Automatic Reference Counting @ Pragma Night
Giuseppe Arici
 
PDF
Elegant Solutions For Everyday Python Problems - PyCon Canada 2017
Nina Zakharenko
 
PDF
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina Zakharenko
Nina Zakharenko
 
PDF
Découvrir dtrace en ligne de commande.
CocoaHeads France
 
PDF
Diving into HHVM Extensions (PHPNW Conference 2015)
James Titcumb
 
PDF
Doing It Wrong with Puppet -
Puppet
 
PPTX
Advance JS and oop
Abuzer Firdousi
 
PDF
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
Fernando Hamasaki de Amorim
 
PDF
Replacing "exec" with a type and provider: Return manifests to a declarative ...
Puppet
 
PDF
JavaScript Functions
Colin DeCarlo
 
PDF
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
Guilherme Blanco
 
PDF
TYPO3 Flow 2.0 Workshop T3BOARD13
Robert Lemke
 
PDF
Foomo / Zugspitze Presentation
weareinteractive
 
PDF
Puppet @ Seat
Alessandro Franceschi
 
PDF
Object Oriented PHP - PART-2
Jalpesh Vasa
 
KEY
Know yourengines velocity2011
Demis Bellot
 
PPTX
WPF and Prism 4.1 Workshop at BASTA Austria
Rainer Stropek
 
KEY
Perl: Hate it for the Right Reasons
Matt Follett
 
PPTX
Parallel and Async Programming With C#
Rainer Stropek
 
ODP
JavaFXScript
webuploader
 
Automatic Reference Counting @ Pragma Night
Giuseppe Arici
 
Elegant Solutions For Everyday Python Problems - PyCon Canada 2017
Nina Zakharenko
 
Elegant Solutions for Everyday Python Problems Pycon 2018 - Nina Zakharenko
Nina Zakharenko
 
Découvrir dtrace en ligne de commande.
CocoaHeads France
 
Diving into HHVM Extensions (PHPNW Conference 2015)
James Titcumb
 
Doing It Wrong with Puppet -
Puppet
 
Advance JS and oop
Abuzer Firdousi
 
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
Fernando Hamasaki de Amorim
 
Replacing "exec" with a type and provider: Return manifests to a declarative ...
Puppet
 
JavaScript Functions
Colin DeCarlo
 
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
Guilherme Blanco
 
TYPO3 Flow 2.0 Workshop T3BOARD13
Robert Lemke
 
Foomo / Zugspitze Presentation
weareinteractive
 
Puppet @ Seat
Alessandro Franceschi
 
Object Oriented PHP - PART-2
Jalpesh Vasa
 
Know yourengines velocity2011
Demis Bellot
 
WPF and Prism 4.1 Workshop at BASTA Austria
Rainer Stropek
 
Perl: Hate it for the Right Reasons
Matt Follett
 
Parallel and Async Programming With C#
Rainer Stropek
 
JavaFXScript
webuploader
 
Ad

Viewers also liked (20)

PDF
7 lessons to learn of Brazil defeat, to your company
Alexandre Uehara
 
PDF
Data Science For Dummies From a Dummy
Eduardo Felipe Ewert Bonet
 
PPTX
Data science for advanced dummies
Saurav Chakravorty
 
PDF
Quero trabalhar com big data data science, como faço-
Alexandre Uehara
 
PDF
Data Science & Big Data, made in Switzerland
Thilo Stadelmann
 
PPTX
Introduction to Machine Learning
Raveen Perera
 
PDF
7 historical software bugs
Alexandre Uehara
 
PDF
Europython - Machine Learning for dummies with Python
Javier Arias Losada
 
PPTX
Introduction of Machine Learning
Mohammad Hossain
 
PDF
TDC2016SP - Trilha Data Science
tdc-globalcode
 
ODP
Introduction to Machine learning
Knoldus Inc.
 
PPTX
Elastically scalable architectures with microservices. The end of the monolith?
Javier Arias Losada
 
PDF
[Eestec] Machine Learning online seminar 1, 12 2016
Grigoris C
 
PDF
Introduction Machine Learning by MyLittleAdventure
mylittleadventure
 
PDF
Pybcn machine learning for dummies with python
Javier Arias Losada
 
PDF
Brief introduction to Machine Learning
CodeForFrankfurt
 
PDF
Pivotal Digital Transformation Forum: Data Science
VMware Tanzu
 
PPTX
Machine learning for dummies - Azuges November 2016
Carlos Landeras Martínez
 
PDF
Machine learning for dummies
Alexandre Uehara
 
PPTX
Introduction to Machine Learning
James Ward
 
7 lessons to learn of Brazil defeat, to your company
Alexandre Uehara
 
Data Science For Dummies From a Dummy
Eduardo Felipe Ewert Bonet
 
Data science for advanced dummies
Saurav Chakravorty
 
Quero trabalhar com big data data science, como faço-
Alexandre Uehara
 
Data Science & Big Data, made in Switzerland
Thilo Stadelmann
 
Introduction to Machine Learning
Raveen Perera
 
7 historical software bugs
Alexandre Uehara
 
Europython - Machine Learning for dummies with Python
Javier Arias Losada
 
Introduction of Machine Learning
Mohammad Hossain
 
TDC2016SP - Trilha Data Science
tdc-globalcode
 
Introduction to Machine learning
Knoldus Inc.
 
Elastically scalable architectures with microservices. The end of the monolith?
Javier Arias Losada
 
[Eestec] Machine Learning online seminar 1, 12 2016
Grigoris C
 
Introduction Machine Learning by MyLittleAdventure
mylittleadventure
 
Pybcn machine learning for dummies with python
Javier Arias Losada
 
Brief introduction to Machine Learning
CodeForFrankfurt
 
Pivotal Digital Transformation Forum: Data Science
VMware Tanzu
 
Machine learning for dummies - Azuges November 2016
Carlos Landeras Martínez
 
Machine learning for dummies
Alexandre Uehara
 
Introduction to Machine Learning
James Ward
 
Ad

Similar to ES6 metaprogramming unleashed (20)

PPTX
Metaprogramming in ES6
Héctor Pablos López
 
PDF
Metaprogramming in JavaScript
Mehdi Valikhani
 
PPTX
ES6 Overview
Bruno Scopelliti
 
PPTX
Fullstack Conference - Proxies before proxies: The hidden gems of Javascript...
Tim Chaplin
 
PPTX
LinkedIn TBC JavaScript 100: Functions
Adam Crabtree
 
PDF
Proxies are Awesome!
Brendan Eich
 
PPTX
JavaScript (without DOM)
Piyush Katariya
 
PDF
EcmaScript 6 - The future is here
Sebastiano Armeli
 
ODP
ES6 PPT FOR 2016
Manoj Kumar
 
KEY
JavaScript Growing Up
David Padbury
 
PPTX
Javascript ES6
Huy Doan
 
PDF
ECMAScript 6 new features
GephenSG
 
PPTX
ECMAScript 2015
Sebastian Pederiva
 
PDF
FalsyValues. Dmitry Soshnikov - ECMAScript 6
Dmitry Soshnikov
 
PDF
JavaScript Primer
Daniel Cousineau
 
PPT
JavaScript - Programming Languages course
yoavrubin
 
PPTX
Александр Скачков "JavaScript metaprogramming with ES2015 Proxy"
Fwdays
 
PPTX
JavaScript Metaprogramming with ES 2015 Proxy
Alexandr Skachkov
 
PPTX
Awesomeness of JavaScript…almost
Quinton Sheppard
 
PDF
ES6: The future is now
Sebastiano Armeli
 
Metaprogramming in ES6
Héctor Pablos López
 
Metaprogramming in JavaScript
Mehdi Valikhani
 
ES6 Overview
Bruno Scopelliti
 
Fullstack Conference - Proxies before proxies: The hidden gems of Javascript...
Tim Chaplin
 
LinkedIn TBC JavaScript 100: Functions
Adam Crabtree
 
Proxies are Awesome!
Brendan Eich
 
JavaScript (without DOM)
Piyush Katariya
 
EcmaScript 6 - The future is here
Sebastiano Armeli
 
ES6 PPT FOR 2016
Manoj Kumar
 
JavaScript Growing Up
David Padbury
 
Javascript ES6
Huy Doan
 
ECMAScript 6 new features
GephenSG
 
ECMAScript 2015
Sebastian Pederiva
 
FalsyValues. Dmitry Soshnikov - ECMAScript 6
Dmitry Soshnikov
 
JavaScript Primer
Daniel Cousineau
 
JavaScript - Programming Languages course
yoavrubin
 
Александр Скачков "JavaScript metaprogramming with ES2015 Proxy"
Fwdays
 
JavaScript Metaprogramming with ES 2015 Proxy
Alexandr Skachkov
 
Awesomeness of JavaScript…almost
Quinton Sheppard
 
ES6: The future is now
Sebastiano Armeli
 

Recently uploaded (20)

PPTX
Materi_Pemrograman_Komputer-Looping.pptx
RanuFajar1
 
PPTX
Why Use Open Source Reporting Tools for Business Intelligence.pptx
Varsha Nayak
 
PDF
A REACT POMODORO TIMER WEB APPLICATION.pdf
Michael624841
 
PPTX
10 Hidden App Development Costs That Can Sink Your Startup.pptx
Lunar Web Solution
 
PDF
How to Seamlessly Integrate Salesforce Data Cloud with Marketing Cloud.pdf
NSIQINFOTECH
 
PPT
Overview of Oracle Receivables Process.ppt
nbvreddy229
 
PDF
Micromaid: A simple Mermaid-like chart generator for Pharo
ESUG
 
PDF
The Future of Smart Factories Why Embedded Analytics Leads the Way
Varsha Nayak
 
PDF
Why Should Businesses Extract Cuisine Types Data from Multiple U.S. Food Apps...
devilbrown689
 
PDF
QAware_Mario-Leander_Reimer_Architecting and Building a K8s-based AI Platform...
QAware GmbH
 
PPTX
AIRLINE PRICE API | FLIGHT API COST |
philipnathen82
 
PDF
Solar Panel Installation Guide – Step By Step Process 2025.pdf
CRMLeaf
 
PPT
Order to Cash Lifecycle Overview R12 .ppt
nbvreddy229
 
PDF
Comprehensive Salesforce Implementation Services.pdf
VALiNTRY360
 
PDF
The Role of Automation and AI in EHS Management for Data Centers.pdf
TECH EHS Solution
 
PPTX
introduction to dart --- Section one .pptx
marknaiem92
 
PPTX
Audio Editing and it's techniques in computer graphics.pptx
fosterbayirinia3
 
PDF
Why Use Open Source Reporting Tools for Business Intelligence.pdf
Varsha Nayak
 
PPTX
AI-Ready Handoff: Auto-Summaries & Draft Emails from MQL to Slack in One Flow
bbedford2
 
PDF
Community & News Update Q2 Meet Up 2025
VictoriaMetrics
 
Materi_Pemrograman_Komputer-Looping.pptx
RanuFajar1
 
Why Use Open Source Reporting Tools for Business Intelligence.pptx
Varsha Nayak
 
A REACT POMODORO TIMER WEB APPLICATION.pdf
Michael624841
 
10 Hidden App Development Costs That Can Sink Your Startup.pptx
Lunar Web Solution
 
How to Seamlessly Integrate Salesforce Data Cloud with Marketing Cloud.pdf
NSIQINFOTECH
 
Overview of Oracle Receivables Process.ppt
nbvreddy229
 
Micromaid: A simple Mermaid-like chart generator for Pharo
ESUG
 
The Future of Smart Factories Why Embedded Analytics Leads the Way
Varsha Nayak
 
Why Should Businesses Extract Cuisine Types Data from Multiple U.S. Food Apps...
devilbrown689
 
QAware_Mario-Leander_Reimer_Architecting and Building a K8s-based AI Platform...
QAware GmbH
 
AIRLINE PRICE API | FLIGHT API COST |
philipnathen82
 
Solar Panel Installation Guide – Step By Step Process 2025.pdf
CRMLeaf
 
Order to Cash Lifecycle Overview R12 .ppt
nbvreddy229
 
Comprehensive Salesforce Implementation Services.pdf
VALiNTRY360
 
The Role of Automation and AI in EHS Management for Data Centers.pdf
TECH EHS Solution
 
introduction to dart --- Section one .pptx
marknaiem92
 
Audio Editing and it's techniques in computer graphics.pptx
fosterbayirinia3
 
Why Use Open Source Reporting Tools for Business Intelligence.pdf
Varsha Nayak
 
AI-Ready Handoff: Auto-Summaries & Draft Emails from MQL to Slack in One Flow
bbedford2
 
Community & News Update Q2 Meet Up 2025
VictoriaMetrics
 

ES6 metaprogramming unleashed

  • 2. are we ready? Metaprogramming is powerful and fun, but remember: "With great power comes great responsibility"
  • 3. about me Javier Arias Losada, senior software engineer at Telefonica. Currently working at EyeOS, helping to create an VDI (Virtual Desktop Infrastructure) platform. @javier_arilos http://about.me/javier.arilos
  • 4. metaprogramming “The key property of metaprograms is that manipulate other programs or program representations.” - Gregor Kiczales
  • 5. metaprogramming levels Meta level: program manipulating other program Base level: program manipulated
  • 6. metaprogramming samples Compiler/transpilers: gcc, coffeescript… Macro languages (eg. C preprocessor) Using "eval" to execute a string as code Database scaffolding/ORM: mongoose, … IDEs: Webstorm, …
  • 7. reflective metaprogramming A program that modifies itself - Reflective Metaprogramming in JS- This is the subject of the talk! MMM… very interesting … but when are we going to JS?
  • 8. Introspection Introspection: read the structure of a program. Object.keys() var obj = { //Base level name: 'Santa', hello: function() { console.log('Hello', this.name,'!'); } }; Object.keys(obj).forEach(function(prop) { console.log(prop); //Meta level });
  • 9. Self-modification Self-modification: change program structure. function renameProperty(obj, oldName, newName) { obj[newName] = obj[oldName]; delete obj[oldName]; } renameProperty(obj, 'hello', 'greet');
  • 10. Intercession Intercession: redefine semantics of some language operations. Object.defineProperty(obj, "roProp", { value: 101, writable: false, enumerable: true}); obj.roProp = 102 obj.roProp //101
  • 11. ‘Cheating’ quine: (function a(){console.log('('+a+')()')})() ‘Cheating’ quine: (function a(){console.log('('+a+')()')})() A real one: _='"'+";document.write('_=',_[17],_[0],_[17],'+',_,_)";document.write('_=',_[17],_ [0],_[17],'+',_,_) metaprogramming fun: Quines Quine: a program that prints a copy of its own source code (no input allowed)
  • 12. Caffeine Facts: A cup of coffee works just 10 minutes after you drink it. It takes 45 minutes for 99% of caffeine to be absorbed. Please… awake people around you!!
  • 13. JS metaprogramming up to ES5 THE GOOD: object metaprogramming API THE UGLY: function metaprogramming THE BAD: eval
  • 14. The Good: Object metapgrming API ● modify property access: ○ getters & setters ○ property descriptors ● Object mutability: preventExtensions, seal, freeze
  • 15. Sample: Test Spy Test Spy myFunction [1] myFunction = spy (myFunction) [5] assert eg. calledOnce [2] myFunction(1, ‘a’) Test spy is a function that records calls to a spied function - SinonJS [3] store call [4] call
  • 16. // we define a very interesting function function sayHi(name){ console.log('Hi '+name+'!') } // now we Spy on sayHi function. sayHi = functionSpy(sayHi); console.log('calledOnce?', sayHi.once); // false. Note that ‘once’ looks like a property!! sayHi('Gregor'); // calling our Function!! console.log('calledOnce?', sayHi.once); // true Sample: Test Spy
  • 17. accessor properties sample - Spy function functionSpy(func){ var proxyFunc = function () { //intercept and count calls to func proxyFunc._callCount += 1; return func.apply(null, arguments); }; Object.defineProperty(proxyFunc, "_callCount", {value: 0, writable: true}); Object.defineProperty(proxyFunc, "once", {get: function(){return this._callCount==1}); return proxyFunc; }
  • 18. The Bad: eval avoid using eval in the browser for input from the user or your remote servers (XSS and man-in-the-middle) “is sometimes necessary, but in most cases it indicates the presence of extremely bad coding.” - Douglas Crockford
  • 19. The Ugly: func metaprogramming ● Function constructor ● Function reflection: ○ Function.prototype.length ○ Ugly hacks
  • 20. var remainder = new Function('a', 'b', 'return a % b;'); remainder(5, 2); // 1 function constructor Seems similar to eval but…
  • 21. function constructor vs eval function functionCreate(aParam) { //Func consctructor cannot access to the closure var funcAccessToClosure = Function('a', 'b', 'return a + b + aParam'); return funcAccessToClosure(1, 2); } functionCreate(3); //ReferenceError: aParam is not defined function functionInEval(aParam) {//has access to the closure eval("function funcAccessToClosure(a, b){return a + b + aParam}"); return funcAccessToClosure(1, 2); } functionInEval(3); // 6 var aParam = 62; //Now, define aParam. functionCreate(3); // 65 functionInEval(3); // 6
  • 22. function constructor vs eval function functionCreate(aParam) { //Func consctructor cannot access to the closure var funcAccessToClosure = Function('a', 'b', 'return a + b + aParam'); return funcAccessToClosure(1, 2); } functionCreate(3); //ReferenceError: aParam is not defined function functionInEval(aParam) {//has access to the closure eval("function funcAccessToClosure(a, b){return a + b + aParam}"); return funcAccessToClosure(1, 2); } functionInEval(3); // 6 var aParam = 62; //Now, define aParam. functionCreate(3); // 65 functionInEval(3); // 6
  • 23. function reflection - length Function.length returns the number of parameters of a function. Usage example: Express checking middlewares signature
  • 24. function parameters reflection We want to get informaton about function parameters. Parameters belong to function signature. Arguments are passed to a function call.
  • 25. function parameters reflection Is it a bad joke? Function.toString() + RegExp to the rescue! How do we do that in JS?
  • 26. DI container implementation Defined in ES5 and ES2015 specs.
  • 27. getParameters : function(func) { //The regex is from Angular var FN_PARAMS = /^functions*[^(]*(s*([^)]*))/m; var funcParams = func.toString().match(FN_PARAMS)[1].split(','); return funcParams; } fucntion parameters reflection Some libraries with Dependency Injection, such as Angular.js use this technique:
  • 28. Eyes still opened? Coffee must be working!
  • 29. ES6 and Proxy The Proxy can define custom behavior for fundamental operations. property lookup, assignment, enumeration, function invocation
  • 30. Proxy concepts handler: interceptor. traps per operation. proxy & handler target A Proxy wraps a target object. target: proxied object.
  • 31. proxy sample: noSuchPropertyze var myObj = { a: 1, b: 'nice' }; myObj = noSuchPropertyze(myObj); // We want to INTERCEPT access to properties (get) myObj.b; // nice myObj.nonExistingProperty; // Error
  • 32. function noSuchPropertyze(obj) { var handler = { get: function(target, name, receiver){ if(name in target) return target[name]; throw new Error('Not found:' + name); } }; return new Proxy(obj, handler); } var myObj = noSuchPropertyze({a: 1, b: 'nice'}); myObj.b; // nice myObj.nonExistingProperty; // Error proxy sample: noSuchPropertyze proxy & handler target myObj[name]
  • 33. Proxy usages Why do we need proxies? virtual objects: persistent or remote objects emulate the dreaded "host objects" do fancy things such as DSLs
  • 34. proxy sample: DRY REST Client // REST client let myRestClient = { getClient: function(id) { console.log('HTTP GET /server/client/'( id ? '/'+id : '')); return 200; }, getBill: function(id) { console.log('HTTP GET /server/bill/'( id ? '/'+id : '')); return 200; }, // (...) DO YOU SEE THE PATTERN? } myRestClient.allo = 7; myRestClient.getClient('kent.beck'); //200 "HTTP GET /server/client/kent.beck" myRestClient.allo; // 7;
  • 35. proxy sample: DRY REST Client function prepareGetter(resource) { return function resourceGetter(id) { console.log('HTTP GET /server/'+resource+( id ? '/'+id : '')); return 200; }} let proto = new Proxy({}, { get(target, name, receiver) { if(name.startsWith('get')) { return prepareGetter(name.slice(3).toLowerCase());} return target[name]; }}); let myRestClient = Object.create(proto); //Prototype is a Proxy myRestClient.allo = 7; myRestClient.getClient('kent.beck'); //200 "HTTP GET /server/client/kent. beck" myRestClient.allo; // 7;
  • 37. DSL with Proxies- implementation // ==== to(3).double.pow.get === var to = (function closure() { // closure for containing our context var functionsProvider = { //Object containing our functions double: function (n) { return n*2 }, pow: function (n) { return n*n } }; return function toImplementation(value) { // Magic happens here! // (...) implementation return new Proxy(functionsProvider, handler); } }());
  • 38. DSL with Proxies- implementation // ==== to(3).double.pow.get === return function toImplementation(value) { var pipe = []; //stores functions to be called var handler = { get(target, fnName, receiver) { if (fnName in target){ //eg. .double : get the function and push it pipe.push(target[fnName]); return receiver;} //receiver is our Proxy object: to(3) if (fnName == "get") return pipe.reduce(function (val, fn) { return fn(val) }, value); throw Error('Method: '+ fnName +' not yet supported'); }, set(target, fnName, fn, receiver) { target[fnName] = fn;} //dynamic declaration of functions }; return new Proxy(functionsProvider, handler);}}());
  • 39. DSL with Proxies- implementation // ==== to(3).double.pow.get === return function toImplementation(value) { var pipe = []; //stores functions to be called var handler = { get(target, fnName, receiver) { if (fnName in target){ //eg. .double : get the function and push it pipe.push(target[fnName]); return receiver;} //receiver is our Proxy object: to(3) if (fnName == "get") return pipe.reduce(function (val, fn) { return fn(val) }, value); throw Error('Method: '+ fnName +' not yet supported'); }, set(target, fnName, fn, receiver) { target[fnName] = fn;} //dynamic declaration of functions }; return new Proxy(functionsProvider, handler);}}());
  • 40. DSL with Proxies- implementation // ==== to(3).double.pow.get === return function toImplementation(value) { var pipe = []; //stores functions to be called var handler = { get(target, fnName, receiver) { if (fnName in target){ //eg. .double : get the function and push it pipe.push(target[fnName]); return receiver;} //receiver is our Proxy object: to(3) if (fnName == "get") return pipe.reduce(function (val, fn) { return fn(val) }, value); throw Error('Method: '+ fnName +' not yet supported'); }, set(target, fnName, fn, receiver) { target[fnName] = fn;} //dynamic declaration of functions }; return new Proxy(functionsProvider, handler);}}());
  • 41. DSL with Proxies - new methods to(2).triple.get; //Error: Method: triple not yet supported to().triple = function(n) {return n*3}; //Add new method: triple to(2).triple.get; // 6
  • 42. That’s all folks! No animals were harmed in the preparation of this presentation.
  • 43. references ● Alex Rauschmayer on Proxies: http://www.2ality.com/2014/12/es6-proxies.html ● About quines: http://c2.com/cgi/wiki?QuineProgram ● Kiczales on metaprogramming and AOP: http://www.drdobbs.com/its-not-metaprogramming/184415220 ● Brendan Eich. Proxies are awesome: http://www.slideshare.net/BrendanEich/metaprog-5303821 ● eval() isn’t evil, just misunderstood: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil- just-misunderstood/ ● On DI: http://krasimirtsonev.com/blog/article/Dependency-injection-in-JavaScript ● Express middlewares: http://expressjs.com/guide/using-middleware.html ● Proxies by Daniel Zautner: http://www.dzautner.com/meta-programming-javascript-using-proxies/
  • 44. Media ● Storm by Kelly Delay: https://flic.kr/p/seaiyf ● The complete explorer: https://www.flickr.com/photos/nlscotland/ ● Record Player by Andressa Rodrigues: http://pixabay.com/en/users/AndressaRodrigues-40306/ ● Wall by Nicole Köhler: http://pixabay.com/en/users/Nikiko-268709/ ● Remote by Solart: https://pixabay.com/en/users/solart-621401/ ● Rocket launch by Space-X: https://pixabay.com/en/users/SpaceX-Imagery-885857/ ● Coffee by Skeeze: https://pixabay.com/en/users/skeeze-272447/ ● Sleeping Monkey by Mhy: https://pixabay.com/en/users/Mhy-333962/ ● Funny Monkey by WikiImages: https://pixabay.com/en/users/WikiImages-1897 ● Lemur by ddouk: https://pixabay.com/en/users/ddouk-607002/
  • 46. function sayHi(name){ console.log('Hi '+name+'!') }// we define a very interesting function sayHi = functionSpy(sayHi);// now we Spy on sayHi function. console.log('calledOnce?', sayHi.once); // false. Note that ‘once’ looks like a property!! sayHi('Gregor'); // calling our Function!! console.log('calledOnce?', sayHi.once); // true function functionSpy(func){ var proxyFunc = function () { //intercept and count calls to func proxyFunc._callCount += 1; return func.apply(null, arguments); }; Object.defineProperty(proxyFunc, "_callCount", {value: 0, writable: true}); Object.defineProperty(proxyFunc, "once", {get: function(){return this._callCount==1}); return proxyFunc; } Test Spy
  • 47. DI container ● Function reflection (parameters) eg: Dependency Injection var Injector = {dependencies: {}, add : function(qualifier, obj){ this.dependencies[qualifier] = obj;}, get : function(func){ var obj = Object.create(func.prototype); func.apply(obj, this.resolveDependencies(func)); return obj;}, resolveDependencies : function(func) { var args = this.getParameters(func); var dependencies = []; for ( var i = 0; i < args.length; i++) { dependencies.push(this.dependencies[args[i]]);} return dependencies;}, getParameters : function(func) {//This regex is from require.js var FN_ARGS = /^functions*[^(]*(s*([^)]*))/m; var args = func.toString().match(FN_ARGS)[1].split(','); return args;}}; var aFancyLogger = { log: function(log){console.log(Date().toString()+" => "+ log);} }; var ItemController = function(logger){ this.logger = logger; this.doSomething = function(item){this.logger.log("Item["+item.id+"] handled!");}; }; Injector.add("logger", aFancyLogger); //register logger into DI container var itemController = Injector.get(ItemController); //get Item controller from DI itemController.doSomething({id : 5});
  • 48. DSL with Proxies var to = (function closure() { var functionsProvider = { double: function (n) { return n*2 }, pow: function (n) { return n*n } }; return function toImplementation(value) { var pipe = []; var handler = { get(target, fnName, receiver) { if (fnName == "get") return pipe.reduce(function (val, fn) { return fn(val) }, value); if (fnName in target) { pipe.push(target[fnName]); return receiver;} throw Error('Method: '+ fnName +' not yet supported'); }, set(target, fnName, fn, receiver) { target[fnName] = fn;} //dynamic declaration of functions }; return new Proxy(functionsProvider, handler);}}()); console.log('to(3).double.pow.get::',to(3).double.pow.get); // 36 console.log('to(2).triple::', to(2).triple.get); //Error: Method: triple not yet supported to().triple = function(n) {return n*3}; console.log('to(2).triple::',to(2).triple.get);

Editor's Notes

  • #5: Programs have a domain. Shipping system domain are customers, orders… Metaprograms domain is programs
  • #34: people need to extend javascript, not only with c++ with a lot of security issues, but with javascript itself in order to continue to extend the language and the libraries in a way that they look as built-in objects. ant that can virtualize/substitute builtin objects. that's very important when you implement libraries. we want to rebuild the world Smalltalk had, we want to get to the very low native code (C++) nirvana. the proxies here are brilliant, otherwise I do not know how we will get there.