OO javascript - context / scope of a self-emitting function in a constructor function

I have a quick question regarding the context / scope when using the self-starting function anon inside the Constructor function.

Observe the following working code:

function Foo() { this.height = 10; this.width = 10; this.init = function() { this.create(); }; this.create = function() { alert('test'); }; } var span1 = new Foo(); span1.init(); 

A warning is displayed in accordance with the forecast. However, I do not want to call span1.init at the bottom. I would prefer the init function in the Constructor function to execute independently. This will give me the code as below:

 function Foo() { this.height = 10; this.width = 10; this.init = (function() { this.create(); })(); this.create = function() { alert('test'); }; } var span1 = new Foo(); 

However, after some Googling, it would seem, using self-execution, it gives a global scope and therefore this.create does not exist in a global sense.

I think I need to do something with call / apply, but I'm not sure what and where exactly.

Any pointers would be much appreciated.

Cheers, Ad.

+4
source share
5 answers

Adi, there are two problems with what you did in this example. The first problem is that inside the directly called function this === window .

The second problem is that these are all functional expressions. Thus, they are all defined in a string.

 function makeIt () { this.a = function () { this.b(); }; this.b = function () { console.log("B"); }; } 

This will work due to late static binding. This means that inside a browser does not know what this refers to until the function is called. He then finds the object to which this is currently relevant.

Otherwise, it will be similar to assigning variables:

 function makeIt () { this.a = this.b; this.b = "George"; } 

There you will get an error. What for? Just because by the time you assign a , b doesn't matter yet.

 function Foo () { this.init = (function (context) { context.change(); }(this)); this.change = function () { doStuff(); }; } 

So what is the problem with this statement? Well, immediately calling functions are functions that call immediately . This means that although we solved this problem by passing this value as a parameter to the inner area ...

... we ask him to run something that does not yet exist.

 function Foo () { this.change = function () { doStuff(); }; this.init = (function (context) { context.change(); }(this)); } 

This should work fine. ... but...

... why did you bother to do it that way? As in, why do you give it a public init property (which is undefined ) when you want it to be automatically built?

Why is init undefined ? Since you are not returning anything, you are launching the function and setting init to the return value of the function, but not returning anything, so it sets init to undefined . Why does init exist at all, then?

Two solutions:

 function Foo () { this.change = function () { doStuff(); }; var init = function () { this.change(); }; // other stuff...... init(); } 

or

 function Foo () { this.change = function () { doStuff(); }; // other stuff.... (function (context) { context.change(); /* a bunch of other stuff that would be in init if there was no other stuff, why not just call this.change()? */ }(this)); } 

And to be honest, if init should be closed and run automatically, should create really be public? You are going to call myObj.create(); after it has already been created?

Why not do something like:

 function Foo () { this.public1 = "Bob"; this.public2 = 32; this.publicMethod = function () {}; var create = function () { /* initialize here */ }; create(); } 

Or again, if you do more than just create :

 function Foo () { this.public1 = "Bob"; this.public2 = 32; this.arrayOfThings = []; this.publicMethod = function () {}; var create = function () {}, overclock = function () {}, polish = function () {}; // Initialize Everything (function (context) { var thing; for (/* ... */) { thing = create(); polish(thing); context.arrayOfThings.push(thing); } overclock(context.arrayOfThings); }(this)); } 

Now you have all your functions and your properties and your variables in one area, and you got your initialization in another - all installation logic is separate from the final logic of the object ...... and you can do things like branching your objects based on input parameters (for example, a polymorphic constructor that will change what he gave you, based on what he received while maintaining the same interface - or a stand-alone factory template where all drawings are 100% closed and closed), without The actual assignments look like ifs and fors riots.

You do not need to call the configuration outside the finished object (this means that no one else can call the installation on the finished object to recreate it / reset). And all this was worth one anonymous function that you are going to use on this.init anyway.

+2
source

You can pass the context to a new closure:

 this.init = (function(ctx) { ctx.create(); })(this); 

Note that this is done at the time init declared, so it will not work if you did not assign create before assigning init . In your previous example, this is not a problem, since span1.init() is called manually after create and init have been assigned.

0
source

How it works (see demo: http://jsfiddle.net/Jw8jz/1/ ):

 function Foo() { this.height = 10; this.width = 10; this.init(); } Foo.prototype = { init: function() { this.create(); }, create: function() { alert('test'); } }; var span = new Foo(); 

Learn more about prototype properties.

0
source

Maybe I missed something, but maybe you want:

 function Foo() { this.height = 10; this.width = 10; this.init = function() { this.create(); }; this.create = function() { alert('test'); }; this.init(); } 

Of course, similarly, you can completely remove the init function or make it private. For instance:

 function Foo() { this.height = 10; this.width = 10; this.create = function() { alert('test'); }; this.create(); } 

Also, note that this.init = (function() {})(); sets this.init to undefined because your function returns nothing.

0
source

Yes, call will work ( apply just has a different argument parameter that you don't use at all):

 this.init = (function() { this.create(); // return something? }).call(this); 
0
source

All Articles