Callback when 'class' instance is created - javascript

var foo = (function(){
var c = function(requests) {
bar();
};
c.prototype = {
bar: bar
};
return c;
})();
var f = new foo();
f.baz(function(){
console.log('new instance of foo created');
});
http://jsfiddle.net/LecJM/
I would like to create a callback function which is called when a new instance of the "class" foo is created. Is this possible? Obviously the code above won't compile, I just want to give you an idea of what I'm trying to achieve.

var Foo = function (createdCallback) {
createdCallback();
return this;
};
var bar = new Foo(function () {
console.log("instance created");
});
Is this what you want to achieve?

Something like this?
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Notify
notifyCreated(this);
};
c.prototype = { ... };
var createdCallbacks = [];
c.onCreate = function(callback) {
createdCallbacks.push(callback);
}
function notifyCreated(instance) {
// Note: forEach requires ES5 or a shim
// Just use whatever you want to loop over the array
createdCallbacks.forEach(function(callback) {
callback(instance);
});
}
return c;
})();
// Add callback *before* creating instances
foo.onCreate(function(instance){
console.log('new instance of foo created', instance);
});
// Create an instance
var f = new foo();
Basically, you add a method to foo (and not to foo.prototype) to add a callback. Inside your constructor, you call all registered callbacks (here demonstrated with a separate internal function). To use, you first register a callback and then start creating instances.
EDIT: As requested, with just one callback:
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Notify
notifyCreated(this);
};
c.prototype = { ... };
// Use a dummy callback by default
var notifyCreated = function(){};
c.onCreate = function(callback) {
notifyCreated = callback;
}
return c;
})();
Demo
EDIT 2: Heck, if you're only going to need one callback, you might as well get rid of the onCreate function and just expose the callback as a variable. There are a few downsides to this though:
You can't do input checking, for example you can't test if the callback is actually a function before storing it.
Others can trigger the callback externally through foo.onCreate(anInstance).
If those are not problematic (for example if you're not exposing foo anyway), feel free to use this extremely simple snippet:
var foo = (function(){
var c = function(requests) {
// Initialize your instance
// ...
// Trigger callback
c.onCreate(this);
};
c.prototype = { ... };
// Expose callback on "class"
c.onCreate = function(){};
return c;
})();
// Set callback *before* creating instances
foo.onCreate = function(instance){
console.log('new instance of foo created', instance);
};
// Create an instance
var f = new foo();
Demo

Try this
var foo = function() {
this.baz();
};
foo.prototype.baz = function () {
console.log('new instance of foo created');
};
var f = new foo();

Related

Any way to reseat a Function object's "this" reference BEFORE constructor invocation?

Trying to implement a Javascript sandboxing scheme and now I've run into a bit of a wrinkle. I need to pass a "context" parameter to the code which will essentially serve as a handle to the "global" object, but so far no luck.
To illustrate the problem with a simple example, consider this bit of code:
var foo = new Function(" this.baz = this.mux; return this ");
foo.mux = "mux";
foo.call();
console.log(foo.baz);
console.log(foo.toString());
Output:
$> undefined
$> function anonymous() { this.mux; return this; }
It obviously doesn't work because the Function object doesn't seem to get it's own this like ordinary functions created with new.
So...is there any way to "reseat" a Function's this to point to itself beforehand (or just any other way around the issue)?
EDIT
Okay, so from what I understand from the comments section I'm going to need a constructed object.
var foo = new Function(" return new function(){ this.baz /* = ?? */; return this; } ");
Is there a way to somehow access the enclosing anonymous function's properties? Like "this.mux = foo.mux" (except of course "foo" isn't visible from that scope)?
I think your getting confused on what new Function( does,. It does not create an instance of an object, it just create a function. So like any object instances you will also need to use new on these.
So you need 2 steps..
create the function that you will be creating an object from..
with this function create an instance using new..
Below is a simple example..
var fcreate =
new Function('global', "this.global = global");
var f = new fcreate("hello");
console.log(f.global);
If your not bothered about instances, we can forget about this altogether, and just create a captured scope as a parameter..
eg..
var f = new Function("global", "console.log(global)");
f("This is a global to function");
f("This is another one");
You can pass foo as a parameter of call:
var foo = new Function(" this.baz = this.mux; return this ");
foo.mux = "mux";
foo.call(foo); // <-- this
Edit: Although the code above works, I wouldn't recommend it. You will be better off creating the function/class foo:
var Foo = function(mux){
this.baz = mux;
}
var foo = new Foo("mux");
console.log(foo.baz);
The best I could come up with that actually works.
var foo = new Function(" this.baz = this.mux; return this ");
var context = { mux: "mux" };
foo = foo.bind(context);
foo();
// context.baz == "mux"
Alright so this is in fact doable, and it's basically an extension of Keith's answer:
function verify(condition)
{
console.log(condition === true ? "pass" : "fail");
}
function test()
{
if(!(this instanceof test))
return new test();
var foo = new Function("mux", "return new function(){ this.baz = mux; return this; } ");
var bar = new foo(null);
verify(bar.baz === null);
var zim = new foo(this);
verify(zim.baz === this);
var qud = new foo(global);
verify(qud.baz === global);
};
test();
Output:
pass
pass
pass
A sincere thanks to everyone for helping me figure this one out - cheers!
* EDIT *
As per Keith's comments, the correct implementation would simply be:
function verify(condition)
{
console.log(condition === true ? "pass" : "fail");
}
function test()
{
if(!(this instanceof test))
return new test();
var foo = new Function("mux", "this.baz = mux; return this; ");
var bar = new foo(null);
verify(bar.baz === null);
var zim = new foo(this);
verify(zim.baz === this);
var qud = new foo(global);
verify(qud.baz === global);
};
test();

Issue when trying to inherit all classes referenced by name in a for loop

Well the title is a mouthful but I couldn't come up with a better one, ideas welcome.
Anyhow, I have a javascript object containing classes as properties. I want to create another object which is in every aspect equal to the first one by subclassing it. I'm gonna try to sum it up:
var L1 = {};
L1.Foo = function() {/*...*/};
L1.Bar = function() {/*...*/};
//...
L1.Baz = function() {/*...*/};
var L2 = {};
L2.Foo = function() { L1.Foo.call(this); /*possibily some other code here*/ };
L2.Foo.prototype = Object.create(L1.Foo.prototype);
L2.Foo.prototype.constructor = L2.Foo;
L2.Bar = function() { L1.Bar.call(this); /*possibily some other code here*/ };
L2.Bar.prototype = Object.create(L1.Bar.prototype);
L2.Bar.prototype.constructor = L2.Bar;
//...
L2.Baz = function() { L1.Baz.call(this); /*possibily some other code here*/ };
L2.Baz.prototype = Object.create(L1.Baz.prototype);
L2.Baz.prototype.constructor = L2.Baz;
var foo = new L2.Foo();
console.log(foo); //L2.Foo
var bar = new L2.Bar();
console.log(bar); //L2.Bar
var baz = new L2.Baz();
console.log(baz); //L2.Baz
First, working version.
I told myself: "huh, looks like there a pattern here" so I went and modified my code as follows:
//first 10 lines unaltered
for(prop in L1) {
L2[prop] = function() { L1[prop].call(this); /*Call super method by default,
unless overriden below*/ };
L2[prop].prototype = Object.create(L1[prop].prototype);
L2[prop].prototype.constructor = L2[prop];
}
//Here I decide that I want to override only the constructor
//for Foo, so naturally:
L2.Foo.prototype.constructor = function() {
L1.Foo.call(this);
this.someOtherProperty = "foo";
};
var foo = new L2.Foo();
console.log(foo); //L2.(anonymous function)?
console.log(foo.someOtherProperty); //undefined?
var bar = new L2.Bar();
console.log(bar); //L2.(anonymous function)?
var baz = new L2.Baz();
console.log(baz); //L2.(anonymous function)?
Second, not-so-working version.
What I am getting wrong?
"huh, looks like there a pattern here" so I went and modified my code
as follows:
for(prop in L1) {
L2[prop] = function() { L1[prop].call(this);
You've hit the common closure in a loop problem - all your L2 functions are actually calling L1.Baz on their new instance as prop will have the value "Baz". See the linked question for how to fix this.
Also, notice that none of your constructors does pass its arguments to the super call, which might bite you as well.
Here I decide that I want to override only the constructor for Foo, so
naturally:
L2.Foo.prototype.constructor = function() {
L1.Foo.call(this);
this.someOtherProperty = "foo";
};
What I am getting wrong?
Overwriting the .constructor property on a prototype object does nothing. Your code is still invoking new L2.Foo, not new L2.Foo.prototype.constructor. You might want to have a look at how the new keyword works.
Instead, you really need to replace L2.Foo. This can be done with this pattern:
L2.Foo = (function (original) {
function Foo() {
original.apply(this, arguments); // apply old L2.Foo constructor
this.someOtherProperty = "foo"; // set property
}
Foo.prototype = original.prototype; // reset prototype
Foo.prototype.constructor = Foo; // fix constructor property
return Foo;
})(L2.Foo);
(or you just put your standard pattern from the first version). If this does get too repetitive, you might also do the .prototype and .constructor setup programmatically:
// whole code
var L2 = {
Foo: function() {
L1.Foo.call(this);
this.someOtherProperty = "foo";
}
// … other overwritten constructors
};
for (var prop in L1) {
if (!L2[prop]) // unless overridden above, create default that only…
(function(parent) {
L2[prop] = function() {
parent.apply(this, arguments); // calls super
};
}(L1[prop]));
L2[prop].prototype = Object.create(L1[prop].prototype);
L2[prop].prototype.constructor = L2[prop];
}
This might just be me, but if the subclass needs to be the same in every aspect as the superclass, why bother making a subclass? I'm not entirely clear an what you are trying to achieve from the code.
But just give the class a property of the class itself
e.g. (in plain java)
public class TestInheritClass {
private TestInheritClass tic;
public TestInheritClass getTic() {
return tic;
}
public void setTic(TestInheritClass tic) {
this.tic = tic;
}
}

Run additional action after constructor

Is it possible to change a constructor so that some extra action is run after an object is created. I tried something like:
var origFoo = Foo
Foo = function() {
origFoo.apply(this, arguments);
/* extra actions */
}
Foo.prototype = new origFoo();
but this has several problems like the constructor being run twice or changing the prototype chain.
You are very close. You should assign the Foo.prototype to the origFoo.prototype in order to get the same prototype chain. Everything else is spot on!
Example:
var Foo = function () {
console.log('OriginalFoo');
};
Foo.prototype.method1 = function () {
console.log('Method1');
};
OriginalFoo = Foo;
Foo = function () {
OriginalFoo.apply(this, arguments);
console.log('NewFoo');
};
Foo.prototype = OriginalFoo.prototype;
Foo.prototype.method2 = function () {
console.log('Method2');
};
var x = new Foo();
x.method1();
x.method2();
Demo: http://jsbin.com/ibatah/1/edit?js,console,output
PS: There still is the problem of static-like properties (Foo.prop), but i'm afraid i don't have a solution for that other than copying them one at a time.
EDIT: Solution for special constructors.
Indeed there are constructors which don't like to be called as functions ex: Image. To get over it, you can do the more awkard solution below. You take advantage of the fact that you can return an object from the constructor and it takes the place of the one created with new. In the overridden constructor you must always use this new object when calling methods instead of this.
var Foo = function(a,b,c) {
console.log('OriginalFoo',a,b,c);
};
Foo.prototype.prop1 = 'Property1';
Foo.prototype.method1 = function() {
console.log('Method1', this.prop1);
};
OriginalFoo = Foo;
Foo = function(a,b,c) {
var obj = new OriginalFoo(a,b,c);
obj.init('Changed...'); // or this.init.call(obj,'Changed!');
this.init('Not Changed'); // applies to a discarded object, has no effect
console.log('NewFoo');
return obj;
};
Foo.prototype = OriginalFoo.prototype;
Foo.prototype.prop2 = 'Property2';
Foo.prototype.method2 = function() {
console.log('Method2', this.prop2);
};
Foo.prototype.init = function(param) {
this.prop2 = param;
};
var x = new Foo('1','2','3');
console.log(x.prop1);
console.log(x.prop2);
x.method1();
x.method2();
Demo: http://jsbin.com/ibatah/2/edit?js,console,output

Creating a new empty object in JavaScript

When trying to create a new object, one that is empty for manipulation, I can't get the old data out.
Here is an example of what I've tried:
function Foo() {
this.Bar = Bar;
// etc..
}
var Bar = {
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
}
// etc..
}
Now, when I create a new object:
var foo = new Foo();
var bar = foo.Bar;
bar.addWord("hello",7);
bar.addWord("world",9);
var lorem = new Foo();
var words = lorem.Bar.__words; // This will display {hello:7,world:9} from the
// previous object
I also tried using Object.create() but it was still the same, showing the __words from the previous object.
The object referred to by Bar is shared between each Foo instance.
I don't really see the point of putting the logic into two objects, you can do this with just one constructor function:
function Foo() {
this.__words = {};
}
Foo.prototype.addWord = function(word, amount) {
this.__words[word] = amount;
}
var foo = new Foo();
foo.addWord("hello",7);
var bar = new Foo();
bar.addWord("world",9);
If you really have to separate the functionality, then make Bar a constructor function as well and create a new instance inside the Foo constructor method:
function Foo() {
this.bar = new Bar();
}
function Bar() {
this.__words = {};
}
Bar.prototype.addWord = function(word, amount) {
this.__words[word] = amount;
}
There are couple of problems in the code.
function Foo() {
this.Bar = Bar;
// etc..
}
var Bar = {
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
}
// etc..
}
Bar should be defined before Foo but it might work without problems because variables are actually defined at function scope level which might not be a problem in this particular example.
That said, you're copying an object inside the Foo constructor. Objects are mutables so it won't work. One change to the object will change the other. What you might be able to do is this. Here's an other way to do that without new.
var Bar = {
create: function () {
var obj = Object.create(this);
obj.__words= {};
return obj;
},
addWord: function (word, amount) {
this.__words[word] = amount;
}
...other methods...
};
var Foo = function () {
this.Bar = Bar.create();
// etc..
};
But I have no idea how is supported Object.create in other browsers. This is syntax is the equivalent to what Felix wrote, you could clearly remove the need of two object.
The only big difference is that instead of writing new Bar(). You are actually creating a prototype object and creating an object from the prototype. The create is the constructor.
Foo could be written in the same way and you'd get something like this.
var Bar = {
create: function () {
var obj = Object.create(this);
obj.__words= {};
return obj;
},
addWord: function (word, amount) {
this.__words[word] = amount;
},
...other methods...
};
var Foo = {
create: function () {
var foo = Object.create(this);
foo.Bar = Bar.create();
return foo;
},
....
};
var foo1 = Foo.create();
var foo2 = Foo.create();
foo1.bar.addWord("allo", 3);
I guess it all depends on what you're trying to do but the create method as some advantages over the "new" operators. You could for example create async methods that after object creations execute a callback.
Over here for more informations. In the end it's pretty much the same thing.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create
I can't get the old data out.
Well you are not clearing var Bar which is global actually - meaning you are creating a new Object with var lorem = new Foo(); but giving him the same Bar Object with Foos constructor.
this.Bar = Bar;
if you want a new Object you should give Foo its own Bar Object.
Or include:
__words : {},
addWord : function (word, amount) {
this.__words[word] = amount;
in Foo itself.
Anyways if you just need this once, you could simply empty the words array in Bar.

avoid needing to declare 'var me = this' for javascript prototype functions

Currently, I create objects in javascript by declaring a construction (regular function) then add methods to the prototype like so
function Test(){
}
Test.prototype.test1 = function(){
var me = this;
}
However, I would like to avoid having to declare var me = this at the top of every function. The following seems to work, but seems like it would be very inefficient:
$(document).ready(function(){
var n = 0;
(function(){
function createTest(){
var me;
function Test(){
this.n = n;
this.testArr = [1, 2, 3, 4];
n++;
}
Test.prototype.test1 = function(){
me.test2();
};
Test.prototype.test2 = function(){
alert(me.n);
$.getJSON('test.php', {}, function(reply)
//want to be able to use 'me' here
me.newField = reply;
});
};
var t = new Test();
me = t;
return t;
}
window['createTest'] = createTest;
})();
var t = createTest();
t.test1();
var t2 = createTest();
t2.test1();
t.test1();
});
This code outputs the expected, but is it actually as inefficient as it looks (the Test object being re-declared every time you call createTest())?
Anyhoo, this would seem a bit hacky... is there a completely different way to do this that is better?
EDIT: The real reason I would like to do this is so that callbacks like the one in test2 will have references to the correct this.
What you can do is bind the current this value to a function and store a copy somewhere. (For the sake of efficiency.)
if (!Function.prototype.bind) {
// Most modern browsers will have this built-in but just in case.
Function.prototype.bind = function (obj) {
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () { },
bound = function () {
return self.apply(this instanceof nop ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
nop.prototype = self.prototype;
bound.prototype = new nop();
return bound;
};
}
function Test(n) {
this.n = n;
this.callback = (function () {
alert(this.n);
}).bind(this)
}
Test.prototype.test1 = function () {
this.test2();
}
Test.prototype.test2 = function () {
doSomething(this.callback);
}
function doSomething(callback) {
callback();
}
var t = new Test(2);
t.test1();
I realize your question was not tagged with jQuery, but you are using it in your example, so my solution also utilizes jQuery.
I sometimes use the $.proxy function to avoid callback context. Look at this simple jsfiddle example. Source below.
function Test(){
this.bind();
}
Test.prototype.bind = function(){
$('input').bind('change', $.proxy(this.change, this));
// you could use $.proxy on anonymous functions also (as in your $.getJSON example)
}
Test.prototype.change = function(event){
// currentField must be set from e.target
// because this is `Test` instance
console.log(this instanceof Test); // true
console.log(event.target == $('input')[0]); // true
this.currentField = event.target; // set new field
};
function createTest(){
return new Test();
}
$(function(){ // ready callback calls test factory
var t1 = createTest();
});
Most of the time, I just declare a local variable that references this, wherever I need a reference to this in a callback:
function Foo() {
}
Foo.prototype.bar = function() {
var that=this;
setTimeout(function() {
that.something="This goes to the right object";
}, 5000);
}
Alternatively, you can use bind() like this:
Function Foo() {
this.bar = this.bar.bind(this);
// ... repeated for each function ...
}
Foo.prototype.bar = function() {
}
What this gives you is that every time you create a new Foo instance, the methods are bound to the current instance, so you can use them as callback functions for setTimeout() et al.

Categories

Resources