0

I have function-constructor. I want to control what function (object) can call it. Here'e the example:

function Bar() {

    // foo can be created only here, when Bar is instantiated
    var foo = new Foo();
}

function Foo() {

   // I'd like to have something like this here:
   if (caller != Bar) {
       alert("Not allowed, the caller is not Bar");
       return;
   }
}

var bar = new Bar();    // this is correct, Foo can be created inside Bar
var foo = new Foo();    // prints "Not allowed, the caller is not Bar" and exits

Is it possible to implement in JS? Are there some functions for such kind of control?

What will be created from Foo if the creation will be aborted this way?

Green
  • 28,742
  • 61
  • 158
  • 247

4 Answers4

2

You can't reliably identify the caller in a constructor across browsers, particularly in the new strict mode.

Instead, you can define Foo() inside of Bar() or define them both inside of the same self executing function so that Foo() is not known outside the scope of Bar() and can thus only be created there.

Some examples:

// Bar() is only known within a single scope
var Foo;
(function(){
    Foo = function() {
    }

    function Bar() {
    }
})();


// Bar() is only known inside of Foo()'s constructor
function Foo() {
    function Bar() {
    }
}

You may find this article instructional which discusses various ways of making instance data truly private: http://www.crockford.com/javascript/private.html. It's not exactly the same as what you're asking here, but uses some of the same techniques (hiding private data in a closure).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • but in the second version, the `Bar` is always a different `Bar` and re-created every time. It doesn't look good to me to redefine the whole `Bar` class this way every time a `Foo` is created :p – Esailija Jul 16 '12 at 21:36
  • @Esailija: I doubt that any Javascript compiler ever compiles the same code twice. – vgru Jul 16 '12 at 21:37
  • @Esailija - it's just a local function. Local functions are used all the time in Javascript and make sense whenever the desired scope is local. This is a desirable coding style when you want to limit the scope. It is no different than using a local variable. The code is only compiled once. If you're still concerned about it, then use my first example. – jfriend00 Jul 16 '12 at 21:37
  • @Groo that's not what I said though. Each `Bar` is a different object and created every time, it has nothing to do with compiling code. Furthermore, every function in the `Bar` prototype will be a new object as well and created every time. So you are creating n objects every time which didn't have to be created. And there is no real reason for doing that. – Esailija Jul 16 '12 at 21:37
  • @Esailija - it's the same as using a local variable in javascript which is a well used technique that is actually pretty efficient. – jfriend00 Jul 16 '12 at 21:40
  • @Esailija: local function is not an instance of an object. Nothing is "created", "recreated" nor "redefined". – vgru Jul 16 '12 at 21:40
  • @Groo a function IS an object. `(function(){} !== function(){})` are 2 separate objects and were created just like that. – Esailija Jul 16 '12 at 21:41
  • @Esailija - a local function is very efficient. In fact, it's more efficient to call than a global function (faster name lookup) because the local variable list is looked at before the global list. If you're really concerned, then use my first example or benchmark the performance difference. – jfriend00 Jul 16 '12 at 21:44
  • @Esailija: you are right. For some reason, I always thought that named functions behave differently from anonymous functions (like they were "static" functions, in a way). – vgru Jul 16 '12 at 21:52
  • The jsperf shows actual practical implications at least in firefox. With a class that has 32 methods, it can only be created 500k times per second. For 60 fps, that gives a theoretical cap of 8333 objects created per frame and that's before even doing any real work such as drawing or calculations. The same with actual static functions gives a cap of 1666600 objects per frame which is very comfortable – Esailija Jul 16 '12 at 22:15
  • @Esailija - I think your point survives fixing the comparison, but that doesn't look like a very fair test. Doesn't `a()` need to be an object with 32 methods to be a fair comparison? And should your test be a comparison of the creation of two objects each with 32 methods, not just a pure function call. So, why not use my first option which doesn't have this execution performance penalty? One might wonder why you're creating objects with lots of methods during an animation frame. Shouldn't you be keeping objects around for the lifetime of an animation. – jfriend00 Jul 16 '12 at 22:29
  • @jfriend00 Well I could add `c.prototype = {32 methods}` here in the setup but we both know it doesn't affect the tests. The whole point is that `a` doesn't need to redefine the functions for each object created, where as `b` does. Just listing the function declarations inside the function is enough, if I were to do an actual class inside there, it would look even worse for `b`. I just don't understand why do you want to create n objects instead of 1 object. – Esailija Jul 16 '12 at 22:40
1

You can try something like: (don't think this is cross browser solution however)

var caller = Foo.caller.name;
if (caller != "Bar") {
}

See this answer for more details.

Another option is to have a global variable that is false by default and you assign to true in the functions you want to allow and to a check in that function.

Community
  • 1
  • 1
fanfavorite
  • 5,128
  • 1
  • 31
  • 58
0

If you want to restrict the creation of the Foo object within Bar, then you can define the function with in Bar.

e.g:

function Bar() {
  var Foo = function Foo() {

    // I'd like to have something like this here:
    if (caller != Bar) {
       alert("Not allowed, the caller is not Bar");
       return;
    }
  }
  var foo = new Foo();
    .
    .
    .
    .
}

Now Foo is not visible outside the Bar scope.

Chandu
  • 81,493
  • 19
  • 133
  • 134
0

You could simply not expose Foo:

(function() {



    function Bar() {
        var foo = new Foo();
    }

    function Foo() {

    }

    window.Bar = Bar; //Expose bar to global scope

})();

A function, when called as constructor returns the created object unless you explicitly return a non-primitive value. So having return; would still return the created object.

Esailija
  • 138,174
  • 23
  • 272
  • 326