Refactoring Legacy JavaScript
Code to Use Classes: The Good,
The Bad and The Ugly
Leonardo Silva (IFNMG, Brazil)
Marco Túlio Valente (UFMG, Brazil)
Alexandre Bergel (University of Chile)
ICSR, Salvador 2017
Slide 2
Slide 2 text
JavaScript is dominating the world…
2
http://gittrends.io/
Slide 3
Slide 3 text
The language is prototype-based
3
But it is possible to emulate classes in
JavaScript
JavaScript
Slide 4
Slide 4 text
4
4
function Circle (radius) { // class
this.radius = radius; // attribute
}
Circle.prototype.getArea = function() { // method
return (3.14 * this.radius * this.radius)
}
var myCircle = new Circle (10); // object
Classes in legacy JavaScript (ES5)
Slide 5
Slide 5 text
5
5
class Circle { // class
constructor(radius) {
this.radius = radius; // attribute
}
getArea() { // method
return (3.14 * this.radius * this.radius)
}
}
var myCircle = new Circle (10); // object
Classes in ES6
5
Slide 6
Slide 6 text
6
Motivation
1. There is a large codebase of legacy JavaScript code
2. Developers frequently emulate classes
3. Developers are not fully aware of ES6 new features
Identifying Classes in Legacy JavaScript Code. Journal of Software: Evolution and Process,2017
Growing a language: An empirical study on how (and why) developers use some recently-
introduced and/or recently-evolving JavaScript features. Journal of Systems and Software, 2016
Slide 7
Slide 7 text
7
Goal
We report a study on refactoring emulated classes
in legacy JavaScript code to native ES6 structures
Slide 8
Slide 8 text
8
Empirical Study
We propose and evaluate three rules to migrate
classes from ES5 to ES6 syntax
15
Migration Rule #2 - Subclasses
class C {
…
}
C.prototype = new D();
class C extends D {
…
}
legacy
code
ES6
Slide 16
Slide 16 text
16
Migration Rule #3 - super() calls
class C extends D {
constructor(..) {
D.call(this, p1);
…
}
…
}
class C extends D {
constructor(..) {
super(p1);
…
}
…
}
legacy
code
ES6
Slide 17
Slide 17 text
The Good Parts
Cases that are straightforward to migrate
17
The Bad Parts
Cases that require manual and ad-hoc migration
The Ugly Parts
Cases that cannot be migrated due to limitations of ES6
Migration Process
The Bad Parts
19
1. Accessing this before super
2. Calling class constructors without new
3. Hoisting
4. Alias for method names
Slide 20
Slide 20 text
20
Bad #1: Accessing this before super
// Legacy code
function MinHeap(compareFn) {
this._comparator = compareFn;
...
}
function PriorityQueue() {
MinHeap.call(this, function(a, b) {
return this.priority(a) < this.priority(b) ? -1 : 1;
});
...
}
PriorityQueue.prototype = new MinHeap();
Slide 21
Slide 21 text
21
// Migrated code
class MinHeap {
...
setComparator(compareFn) {
this._comparator = compareFn;
}
}
class PriorityQueue extends MinHeap {
constructor() {
super();
this.setComparator(
(function(a, b) {
return this.priority(a) < this.priority(b) ? -1 : 1;
}).bind(this));
...
}
}
21
Bad #1: Accessing this before super
Slide 22
Slide 22 text
22
Bad #2: Calling class constructors without new
// Class constructor (legacy code)
function Server(srv, opts){
if (!(this instanceof Server))
return new Server(srv, opts);
}
// Client code
var io = Server();
// or
var io = new Server();
Slide 23
Slide 23 text
23
// Migrated code
class _Server{
constructor(srv, opts) { … }
…
}
function Server(srv, opts) {
if (!(this instanceof _Server))
return new _Server(srv, opts);
}
Bad #2: Calling class constructors without new
Slide 24
Slide 24 text
24
Bad #3: Hoisting
// Legacy code
var _tempDisplay = new DisplayObject();
DisplayObject.prototype.getBounds = function(..) {
...
this.parent = _tempDisplay;
}
Slide 25
Slide 25 text
25
// Migrated code
var _tempDisplay = null;
class DisplayObject {
...
}
_tempDisplay = new DisplayObject();
Bad #3: Hoisting
Slide 26
Slide 26 text
26
Bad #4: Alias for method names
// Legacy code
Slick.prototype.addSlide =
Slick.prototype.slickAdd =
function(markup, index, addBefore) { ... };
Slide 27
Slide 27 text
27
// Migrated code
class Slick {
...
slickAdd(markup, index, addBefore) { ... }
// Method alias
addSlide(markup, index, addBefore) {
return slickAdd(markup, index, addBefore); }
}
Bad #4: Alias for method names
Slide 28
Slide 28 text
28
The Bad Parts - Summary
1. Accessing this before super
algorithms.js (2)
pixi.js (1)
2. Calling class constructors
without new
socket.io (1)
3. Hoisting algorithms.js (3)
pixi.js (80)
4. Alias for method names
Slick (25)
socket.io (8)
pixi.js (6)
Slide 29
Slide 29 text
29
The Ugly Parts
1. Getters and setters only known at runtime
(meta-programming)
2. Static data properties
3. Optional features
Slide 30
Slide 30 text
30
Ugly #1: Getters and setters only known at runtime
// Legacy code
flags.forEach(function(flag){
Socket.prototype.__defineGetter__(flag,
function(){ ... });
});
30
32
Ugly #3: Optional features
// Legacy code
var core = require('../core');
core.Container.prototype.getChildByName =
function (name) { ... };
32
32
32
Slide 33
Slide 33 text
33
The Ugly Parts - Summary
1. Getters and setters only known
at runtime (meta-programming)
socket.io (5)
2. Static data properties parallax (28)
pixi.js (14)
3. Optional features pixi.js (6)
33
Slide 34
Slide 34 text
34
Feedback from Developers
34
Slide 35
Slide 35 text
35
Created Pull Requests
System ID # Comments
Opening
Date
Status on
12-Oct-2016
Fastclick #500 0 01-Sep-16 Open
Grunt #1549 2 31-Aug-16 Closed
Slick #2494 5 25-Aug-16 Open
Parallax #159 1 01-Sep-16 Open
Socket.io #2661 4 29-Aug-16 Open
Isomer #87 3 05-Sep-16 Closed
Algorithms.js #117 4 23-Aug-16 Open
Pixi.js #2936 14 09-Sep-16 Merged
Slide 36
Slide 36 text
36
System ID # Comments
Opening
Date
Status on
12-Oct-2016
Fastclick #500 0 01-Sep-16 Open
Grunt #1549 2 31-Aug-16 Closed
Slick #2494 5 25-Aug-16 Open
Parallax #159 1 01-Sep-16 Open
Socket.io #2661 4 29-Aug-16 Open
Isomer #87 3 05-Sep-16 Closed
Algorithms.js #117 4 23-Aug-16 Open
Pixi.js #2936 14 09-Sep-16 Merged
36
• No
comments
• Last
commit
=
April,
2016
• Repository
sparsely
maintained
Created Pull Requests
Slide 37
Slide 37 text
37
System ID # Comments
Opening
Date
Status on
12-Oct-2016
Fastclick #500 0 01-Sep-16 Open
Grunt #1549 2 31-Aug-16 Closed
Slick #2494 5 25-Aug-16 Open
Parallax #159 1 01-Sep-16 Open
Socket.io #2661 4 29-Aug-16 Open
Isomer #87 3 05-Sep-16 Closed
Algorithms.js #117 4 23-Aug-16 Open
Pixi.js #2936 14 09-Sep-16 Merged
37
“We
currently
support
node
0.10
that
does
not
support
this
syntax.
Once
we
are
able
to
drop
node
0.10
we
might
revisit
this.”
37
Created Pull Requests
Slide 38
Slide 38 text
38
System ID # Comments
Opening
Date
Status on
12-Oct-2016
Fastclick #500 0 01-Sep-16 Open
Grunt #1549 2 31-Aug-16 Closed
Slick #2494 5 25-Aug-16 Open
Parallax #159 1 01-Sep-16 Open
Socket.io #2661 4 29-Aug-16 Open
Isomer #87 3 05-Sep-16 Closed
Algorithms.js #117 4 23-Aug-16 Open
Pixi.js #2936 14 09-Sep-16 Merged
38
38
38
Created Pull Requests
• The
comments
suggest
that
they
are
under
evalua*on
Slide 39
Slide 39 text
39
System ID # Comments
Opening
Date
Status on
12-Oct-2016
Fastclick #500 0 01-Sep-16 Open
Grunt #1549 2 31-Aug-16 Closed
Slick #2494 5 25-Aug-16 Open
Parallax #159 1 01-Sep-16 Open
Socket.io #2661 4 29-Aug-16 Open
Isomer #87 3 05-Sep-16 Closed
Algorithms.js #117 4 23-Aug-16 Open
Pixi.js #2936 14 09-Sep-16 Merged
39
“IMHO
the
class
syntax
is
misleading,
as
JS
‘classes’
are
not
actually
classes.
Using
prototypal
paGerns
seems
like
a
simpler
way
to
do
inheritance.”
39
39
39
Created Pull Requests
Slide 40
Slide 40 text
40
System ID # Comments
Opening
Date
Status on
12-Oct-2016
Fastclick #500 0 01-Sep-16 Open
Grunt #1549 2 31-Aug-16 Closed
Slick #2494 5 25-Aug-16 Open
Parallax #159 1 01-Sep-16 Open
Socket.io #2661 4 29-Aug-16 Open
Isomer #87 3 05-Sep-16 Closed
Algorithms.js #117 4 23-Aug-16 Open
Pixi.js #2936 14 09-Sep-16 Merged
40
“Awesome
work!
It
is
really
great
Kming
because
we
were
planning
on
doing
this
very
soon
anyways.”
40
40
40
40
40
Created Pull Requests
Slide 41
Slide 41 text
Final Remarks
• Bad / Ugly cases are present in 75% of the studied systems
• Moving from ES5 to ES6 classes can be challenging
• Developers tend to move to ES6
• There are demands for new class-related features in ES6
• We used the lessons learnt to develop a refactoring tool
41