0

I am writing a class factory that is a function that accepts parameters to define a class and returns a javascript class (function with a 'this' context) that is a named function, like

function ClassName(){
   // ...
}

Its all working great except for the naming of the function. There doesn't seem to be a way to dynamically create a named function.

I admit, this is mearly a stylistic problem and does not alter the functionality of the class.. I just really like seeing the class names in the console, as well as the names of all ancestor classes. It really makes debugging a lot easier.

Currently I am using eval() to accomplish this using code similar to.

function classGen(className, constructorFn, proto){
    var NamedClass;
    eval('NamedClass = function ' + className + '{ constructorFn.apply(this, arguments) };');
    for(var key in proto)
        NamedClass.prototype[key] = proto[key];
    return NamedClass;
}

etc....

Would this be considered a "safe" use of the eval function?

Why / why not?

---edit---

I dont know why i didn't consider

className = className.replace(/[^a-z0-9]/gi, '');
Julian
  • 500
  • 2
  • 10
  • 3
    What problem you are trying to solve with such code? – vittore Feb 28 '14 at 22:45
  • You should check out: http://stackoverflow.com/questions/197769/when-is-javascripts-eval-not-evil – RBZ Feb 28 '14 at 22:45
  • 1
    It's "safe" insofar as you trust `className`. However, with few exceptions, there may be cleaner ways to approach a problem than with `eval` .. also, since `eval` returns a value, if you *must* use it, consider a Function Expression: `var klass = eval('(function ' + className + '{ constructorFn.apply(this, arguments) })');`. – user2864740 Feb 28 '14 at 22:46
  • 1
    It appears that all you're trying to solve with this construction is to keep the user of your API from having to define a function shell with the appropriate name and then pass that to you where your factory can manipulate the prototype. Is that really worth it? – jfriend00 Feb 28 '14 at 22:46
  • The only times I've found `eval()` is actually required in coding and there is not a better way to implement are when you have end-user specified javascript text that you wish to run. And, when you have user defined anything, that is exactly when `eval()` is NOT safe unless it's firewalled off in domain-isolated frames. So, I've never found a good reason to use `eval()` in situations like you are. Plus, `eval()` is slow. – jfriend00 Feb 28 '14 at 22:51
  • 1
    What if you did something like: `var func = {}; func[className] = function(){}`? Would that work? Or am I not understanding this question? If you do something like `var a = function b(){}`, then `b` is only accessible inside that function (it won't be available inside `constructorFn`). – gen_Eric Feb 28 '14 at 22:51
  • 2
    Anybody working on any sort of OO toolkit for Javascript should be familiar with [augment](https://github.com/javascript/augment). You don't have to use it, but it's powerful, simple, and fast, so you should at least steal ideas from it. – Pointy Feb 28 '14 at 23:01
  • @Pointy: Woah! That function is so clean and pretty :-) – gen_Eric Feb 28 '14 at 23:05
  • @RocketHazmat YES!! Thank you!! That make the class names and inherited class names in the console. which was what I was trying to get. – Julian Feb 28 '14 at 23:29
  • @jfriend00 No, it is not worth it. But it's my job. So... – Julian Feb 28 '14 at 23:29
  • @Julian - then as others have said, the use of `eval()` is only as safe as the origin and safety of the string passed to it. Not using `eval()` avoids this issue entirely. But, if you're going to use it, then you have to thoroughly understand what the possibilities are of rogue code being passed there, what harm could be done with that rogue code that you run `eval()` on what the consequences of that rogue code might be. If there's no significant harm that could be done or the string comes only from a trusted source and you're sure of both, then you may be fine. – jfriend00 Feb 28 '14 at 23:33
  • @jfriend00 Considering the API's function - to create classes to use in your own code... it would be pretty dumb to pass it malicious content. They would only be harming themselves. – Julian Feb 28 '14 at 23:36
  • @Julian - maybe. But suppose a client of your API is doing an app that teaches programming and they prompt the user for the function name. Now it's user generated content that could have any arbitrary JS in it that is getting `eval()` run on it. Maybe still not a problem depending upon the context, but it all depends upon the finer details of what's at risk in the site and what issue it would cause to have arbitrary user-generated JS injected into that point in the site. As a developer, I'd rather use an API that didn't use `eval()`. – jfriend00 Feb 28 '14 at 23:43
  • @Julian - if the above user-generated content was shared with others via the site and could the fault was that they would be injected into other people's view of the site, now you have some JS that could be stealing their personal details or info on the site. That's how some of these exploits get propagated. Maybe you can't envision this type of thing happening in this case, but it's common that the developer doesn't envision how it can get exploited when they first write the code. That's why it's always safer to avoid even opening the door a crack even if you think the coast is clear. – jfriend00 Feb 28 '14 at 23:45
  • @jfriend00 - That's like totally another ballpark from where this script will be used. And think about the situation you described. The client of my api will be using it in an app teaches programming and accepts user input and runs that user input as code.... .. .. The main purpose of that application is to do what you're telling me I should not do. That's what a 'sandbox' is for. – Julian Mar 01 '14 at 02:32
  • @julian - then you can decide it's safe. Only you have enough context to make that call. – jfriend00 Mar 01 '14 at 03:02

3 Answers3

0

user2864740 answered in the comment

It's "safe" insofar as you trust className.

Which makes sense, but also directed my attention to the placement of the user defined "code". The string is being injected between the function keyword and and a set of parentheses. So an error will occur if classname is anything but a valid function name.

Julian
  • 500
  • 2
  • 10
  • many other things could get injected in there such as anything that finishes off the first function definition without an error and then any arbitrary JS after that. – jfriend00 Mar 01 '14 at 05:18
  • What about passing `"a(){};alert('This is a security hole!\\n\\nCookies:\\n\\n'+document.cookie)//"` as the class name. – Toothbrush Mar 08 '14 at 16:56
0

Yes, it is possible!

This code uses a property called displayName on the function, which is supported by Firefox Developer Tools, Firebug, and Google Chrome Developer Tools (partially):

function generateClass(name, constructor, prototype) {
    'use strict';

    var classPrototype = constructor.prototype;

    for (var key in prototype) {
        classPrototype[key] = prototype[key]

        if (typeof classPrototype[key] === 'function') classPrototype[key].displayName = name + '.' + key;
    }

    constructor.displayName = name;

    return constructor;
};

var Person = generateClass('Person', function(name) {
    this.name = name || 'John Smith';
}, {
    sayHello: function() {
        alert('Hello from ' + this.name + '!');
    }
});

// Try these in the console:
Person;
new Person('James Smith');

Note that you can use any string for the displayName, such as var MalePerson = generateClass('My Person', ....

Also, you can change displayName on the fly:

var Person = generateClass('Person', function(name) {
    this.name = name || 'John Smith';
    this.constructor.displayName = 'Person (' + this.name + ')';
}, {
    sayHello: function() {
        alert('Hello from ' + this.name + '!');
    }
});

// Try this in the console:
new Person('James Smith');
Toothbrush
  • 2,080
  • 24
  • 33
0

Where are these functions defined? If they're defined in the global scope, you can do this:

var name="fun";
window[name]=function(){};
window[name].name=name;
Toothbrush
  • 2,080
  • 24
  • 33
HMR
  • 37,593
  • 24
  • 91
  • 160
  • 1
    I answered that on the 28th of February, but I deleted my answer, as @Julian said that wasn't what he wanted to do. Also, you can't set the `name` of a function. `name` is a read-only property. – Toothbrush Mar 08 '14 at 14:42