about:benjie

Random learnings and other thoughts from an unashamed geek

JavaScript: Defining a Window Level Function With Eval()

| Comments

You know the old mantra: eval is evil! Well, maybe, but I hate writing lots of code, and I like my debugging to be simple. I wanted to write a shorter way to extend a parent class into a child class. Long way (too much repeated code/risk of copy & paste errors):

Long winded: manually creating each class from scratch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Root class
function MyClass() {
  return this;
}

// Create first subclass
function MyChildClass1() {
  return this;
}
MyChildClass1.prototype = new MyClass();
MyChildClass1.prototype.parent = MyClass.prototype;
MyChildClass1.prototype.constructor = MyChildClass1;

// Create second subclass
function MyChildClass2() {
  return this;
}
MyChildClass2.prototype = new MyClass();
MyChildClass2.prototype.parent = MyClass.prototype;
MyChildClass2.prototype.constructor = MyChildClass2;

// Create an instance
var instance = new MyChildClass2();

Shorter way (gives wrong output on console.log()):

Shorter, but class names are console.log’d incorrectly
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Automation function
function NS_Extend(parentClass,childClassName) {
  var childClass = function(){return this;}
  childClass.prototype = new parentClass();
  childClass.prototype.parent = parentClass.prototype;
  childClass.prototype.constructor = childClass;
  window[childClassName] = childClass;
}

// Root Class
function MyClass() {
  return this;
}

// First subclass
NS_Extend(MyClass,'MyChildClass1');

// Second subclass
NS_Extend(MyClass,'MyChildClass2');

// Create an instance
var instance = new MyChildClass2();

Simple, but when I console.log(instance) it gives me NS_Extend.childClass as the name of the class, rather than MyChildClass2 as was done previously. It makes no difference to the functionality of the app, but it makes my debugging a bit harder. And I really don’t want to have to keep defining function ClassName(){return this;} everywhere - waste of time typing all that repeated code! So I figured I’d define the function through eval(). However that didn’t work, until I realised that eval() is scoped to the current context. So to define a window level function we’d need to change the scope to the window object. Easy enough with JavaScript’s .call() method. The solution:

Eval isn’t always evil: Fixing the console.log class names.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyClass() {
  return this;
}
function NS_Extend(parentClass,childClassName) {
  var js = 'function '+childClassName+'(){return this;}';
  eval.call(window,js);
  var childClass = window[childClassName];
  childClass.prototype = new parentClass();
  childClass.prototype.parent = parentClass.prototype;
  childClass.prototype.constructor = childClass;
}
NS_Extend(MyClass,'MyChildClass1');
NS_Extend(MyClass,'MyChildClass2');
var instance = new MyChildClass2();

Now when I console.log(instance) it tells me MyChildClass2 - WIN!

[I use Chrome under Linux, by the way.]

Comments