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.
Related
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();
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;
}
}
I am generating a lot of "classes" (actually functions trying to simulate classes as in c# or other object oriented languages), and are looking for the best way to do this.
As you might notice, I also have jQuery available.
This is how all classes are generated at this point:
MyClass = (function() {
function innerClass() {
var self = this;
var myField;
// This function works as the constructor
this.init = function(opts) {
// Arguments to the constructor
var defaultOpts = {
myInitArgument: null
}
opts = $.extend(defaultOpts, opts);
self = this;
// Any custom constructor code is generated here...
}
// A function
this.myFunction = function() {
myField = "Hello World!";
}
// Returns an object with all selected fields and function that should work as "public". Those not mentioned here, will not be visible outside this class.
return {
init: this.init,
myFunction: this.myFunction,
myField: myField,
}
}
return innerClass;
})();
Then I create instances of the class like this:
var myObject = new MyClass();
myObject.init({myInitArgument: 'test'});
My main problem here is that inside the myFunction, "myField" will be set to "Hello World!" if I break in the debugger (i.e. Chrome Developer Tools), but using "myObject.myField" returns undefined.
I made a fiddle if you would like to play around with this sample.
What is the best way to accomplish this problem, and are there perhaps other things you feel of warning me about?
JavaScript is a bit weird when it comes to making classes and objects. IMO, this is the most reliable and readable method of doing it: start with a function that becomes your primitive object (Fruit).
Edit: thanks to #Bergi for pointing out that previous version had vestigial variables, needed to be moved to init().
function Fruit(opts) {
this.init(opts);
}
Now, expand the function, giving it more functions, like init, etc:
Fruit.prototype.init = function(opts) {
// init values go here
this.cost = 0;
this.count = 0;
var that = this; // in the iteration below, we want to refer to our parent
for( k in opts )(function(k, v) {
that[k] = v;
})(k, opts[k]);
}
// now, here's a specialized set of functions that sets properties (price/quant)
// note that they do "return this" - this is so you can conveniently chain
// commands. ex: apple.setPrice(10).setQuantity(5);
Fruit.prototype.setPrice = function(how_much) {
this.cost = how_much;
return(this);
}
Fruit.prototype.setQuantity = function(how_many) {
this.count = how_many;
return(this);
}
Simple function to return a computed value. At this point, once instantiated, the object becomes 'self aware'. Helper functions like this become more readable.
Fruit.prototype.getEarnings = function() {
return( this.cost * this.count );
}
So far we've only setup the abstract structure. To use this, create a new object:
var apple = new Fruit({ genus: 'Malus' });
var orange = new Fruit({ genus: 'Citrus' });
apple.setPrice(1.50).setQuantity(20);
orange.setPrice(1.25).setQuantity(40);
console.info( apple.genus + " will earn you $" + apple.getEarnings() ); // $30
console.info( orange.genus + " will earn you $" + orange.getEarnings() ); // $50
I don't understand what you do that much complicated things to have classes.
var myField and <returned object>.myField are two different variables, modifying one won't change the other.
You can try this (encapsulation):
return {
init: this.init,
myFunction: this.myFunction,
getMyField: function() {return myField;},
}
// ...
$('.console').append('<br />Outside myFunction: ' + myObject.getMyField());
or this (get operator):
return {
init: this.init,
myFunction: this.myFunction,
get myField() {return myField;},
}
// ...
$('.console').append('<br />Outside myFunction: ' + myObject.myField);
This worked fine for me
$('.console').append('Class simulation test:<br />');
// My class
function MyClass() {
var self = this, myField;
// This function works as the constructor
this.init = function(opts) {
// Arguments to the constructor
$('.console').append('<br />Inside myFunction: ' + JSON.stringify(opts));
var defaultOpts = {
myInitArgument: null
}
opts = $.extend(defaultOpts, opts);
//self = this; // no need of this
// Any custom constructor code is generated here...
this.myFunction('Hello from the constructor!');
}
// A function
this.myFunction = function(value) {
this.myField = value; //if you dont use var it will either refer to parent my field or window
$('.console').append('<br />Inside myFunction: ' + this.myField);
};
console.log(JSON.stringify(arguments[0]));
this.init(arguments[0]);
// Returns an object with all selected fields and function that should work as "public". Those not mentioned here, will not be visible outside this class.
return {
myFunction: this.myFunction,
myField: myField,
}
}
// instanciate
var myObject = new MyClass({myInitArgument: 'test'});
// test
myObject.myFunction('Hello from the outside!');
$('.console').append('<br />Outside myFunction: ' + myObject.myField);
I have recently been researching this. I have succeeded, here. The ticker object at that link is a real psuedo class.
var foo = function(args){
this.args = args;
this.whatever = "whatever";
}
foo.prototype.addBar = function(bar){
this.args += bar;
}
foo.prototype.getArgs = function(){
return this.args;
}
var baz = new foo("Hello");
baz.addBar(" Super Man");
var helloStr = baz.getArgs();
//helloStr holds "Hello Super Man"
var what = baz.whatever;
//what holds "whatever"
Simple, no need for inner function, new foo() is the constructor.
Consider this code:
var Foo = function () {
this.bar = [];
this.hello = function () {
this.name = "world";
};
};
for (var property in Foo) {
alert(111);
}
It does nothing. Is there a way I can iterate over properties and public methods of Foo? It would work if Foo was object literal, like this:
var Foo = {
bar: [],
hello: function () {
this.name = "world";
}
};
for (var property in Foo) {
alert(111);
}
But I would prefer for it to be a function instead.
The reason I want to do this, I want to extend from Foo using mixin pattern.
http://jsfiddle.net/ChU2V/
You need an actual instance of Foo for this to work:
var foo = new Foo();
for (var property in foo) {
alert(111);
}
Otherwise, the properties are just "virtual" in the sense, that it's never reached program code.
Other than that, you can define the properties on the prototype:
var Foo = function() {};
Foo.prototype = {
bar: [],
hello: function () {
this.name = "world";
}
};
and then loop over Foo.prototype.
Finally, being a dynamic language, JS also allows you to go completely crazy, if you must:
var possible_props = Foo.toString().match(/\bthis\.\([a-zA-Z0-9_]+)\s*=/g);
// will yield an array similar to this:
// ["this.bar =", "this.hello ="]
Note however, that this is very error-prone and not recommended. For example, it doesn't catch cases like this:
var that = this;
that.baz = null;
for (var property in new Foo()) {
console.log(property);
}
Try
var Foo = function () {
this.bar = [];
this.hello = function () {
this.name = "world";
};
};
for (var property in new Foo() ) {
alert(111);
}
Notice the new Foo().
Fiddle updated.
http://jsfiddle.net/sujesharukil/ChU2V/2/
var fooInstance = new Foo();
for(var property in fooInstance){}
You have to create an instance of Foo in order to get the properties off of it.
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