0

I've got this format prototype method (simplified) and what I wanna do is check if char is alphabetic (case insensitive), and if it's also a function declared in the same scope. If it is, I want to call the function. So if I pass in x it should execute the alert.

I'm gonna be using this method to format a date by the given format string. E.g. format('H:i:s') it would check if H, i, and s is a function and call them.

How can achieve that?

I tried something based on this answer: https://stackoverflow.com/a/359910/1115367

Here's my code:

function Time() {
    //initialization
}

Time.prototype = {
    format: function (char) {
        if (char.test(/[a-z]/i) && typeof window[char] === 'function') { //undefined
           window[char]();
        }

        function x() {
            alert('works');
        }
    }
};

If I pass in a value it returns: Uncaught TypeError: undefined is not a function

Community
  • 1
  • 1
Kid Diamond
  • 2,232
  • 8
  • 37
  • 79
  • so does your code not work? what doesn't it do? – atmd Apr 01 '15 at 12:02
  • It gives me an error `Uncaught TypeError: undefined is not a function`. – Kid Diamond Apr 01 '15 at 12:07
  • what line is the error generated on? if `window[char]` is not a function then it wouldnt get called at `window[char]();` – atmd Apr 01 '15 at 12:08
  • What object type are you trying to add this to the prototype of? By the way, your example is simply replacing the prototype of your `testObject`, instead of adding your `format` function to the existing prototype. – forgivenson Apr 01 '15 at 12:39
  • This *is* the prototype, not a replacement. – Kid Diamond Apr 01 '15 at 12:40
  • So, why does this need to be added to an existing object, instead of creating a new object like I show in my answer? I'm just trying to understand what you are trying to accomplish. – forgivenson Apr 01 '15 at 12:43
  • @forgivenson Because it is related to my `Time` object, and I would like it in the prototype and not the constructor itself. – Kid Diamond Apr 01 '15 at 12:45
  • You might benefit from reading https://github.com/getify/You-Dont-Know-JS/tree/master/scope%20%26%20closures . – James Sumners Apr 01 '15 at 12:50
  • Well, you can just set the prototype of your `Time` class: `Time.prototype = { format: //.... etc };`. Just use `this` instead of `window`, like I did in my answer. But I'm not really sure what that gains you over just adding them as part of the class itself. – forgivenson Apr 01 '15 at 12:55

3 Answers3

0

test() is not a function of strings, it is a function of RegExp. See the documentation for test here: RegExp.prototype.test()

So, create a RegExp object first, then use it to call test, passing in your string, char.

After looking at your code more, I think you need to turn turn your format function into a 'class' (JavaScript doesn't really have classes, but you can create an object from a function).

function Formatter() {
    this.format = function(char) {
        var regex = /[a-z]/i;

        if (regex.test(char) && typeof this[char] === 'function') {
           this[char]();
        }
    };

    this.x = function() {
        alert('works');
    }
}

var formatter = new Formatter();

formatter.format('x');

And here is the working jsfiddle.

As you can see, you create a formatter object, which contains the format function and your x function in its scope, this.

forgivenson
  • 4,394
  • 2
  • 19
  • 28
0

There is no way to retrieve a local variables (or function) by name (as far as I know anyway).

Declared named functions are assigned to a variable of the same name: function thing(){} ~ var thing = function(){}

Global variables are automatically attached to window so you can retrieve them by name using the window object.

However, that is (fortunately) not the case for all other variables.

For example

var global1 = 'thing'; // root level (no wrapping function) -> global

(function(){
    global2 = 'global';     // no 'var' keyword -> global variable (bad practice and not accepted in strict mode)
    var local = 'whatever'; // local variable

    function check(name){
        if(window[name]) console.log('found: '+ name);
        else console.log('nope: '+name);
    }

    check('foo');
    check('global1');
    check('global2');
    check('local');
}());

will output:

[Log] nope: foo
[Log] found: global1
[Log] found: global2
[Log] nope: local

What you can do however is to attach your methods to an object.

Edit

If it is an option, you can also directly pass the function as argument (instead of a string containing its name).

function a(){}

function toCall(f){
   // do stuff with f
}

toCall(a);
Quentin Roy
  • 7,677
  • 2
  • 32
  • 50
0

You can use an Immediately Invoked Function Expression (IIFE) to create your scope:

var Time = (function() {
  var handlers = {
    x: function () {
      console.log('x invoked');
    }
  };

  function Time() {
    // fun stuff
  }

  Time.prototype = {
    format: function(char) {
      handlers[char](); // if char === 'x'
    }
  };

  return Time;
}());
James Sumners
  • 14,485
  • 10
  • 59
  • 77