I was just not sure how to search this out despite many tries, so forgive me if this has been answered before.
The question is simple: can I create an instance of class window.A.class() as window.B?
To clarify, I have an object literal holding all my data for a browser game:
var gameName = {
environment: function() {
this.place = "...";
// ...
// ...
},
game: function() {
this.player = function() {
// ...
}
}
// and so on...
}
Could I create a window-level gameName.environment() instance with var A = new gameName.environment()? Are there any restrictions to creating an object-bound class's instance outside the class' parent object?
It doesn't really matter in this case how/where a function is defined. Consider these two examples:
function Foo() {}
var obj = {
bar: Foo
};
and
var obj = {
bar: function () { }
};
As far as the function and the object are concerned, those two examples are equivalent. So no, there is no problem calling a function assigned to an object property with new. All you need is a reference to the function, it doesn't matter how you get that reference.
You could do
var Environment = gameName.environment;
var A = new Environment();
if you like that better, but that's totally unnecessary.
Related
I've been reading about JavaScript prototypal inheritance and the prototype property, and I started making a fiddle to understand the concepts better. But I need help understanding why my example isn't working as I thought it would.
In the following example, I'm trying to create a hierarchy where when you create an object, it tells you the name of the parent object. However, my logs always return "object".
One article explained that prototype chaining works so that if a property is not found on the object, the prototype of the parent is checked, and it keeps going up until 'Object', and if not found returns undefined.
Here's a fiddle to go along: http://jsfiddle.net/hqozqd0m/
Object.prototype.cname = 'object';
function plant() {
function parentname() { return this.cname; }
return {
parentname:parentname
}
}
plant.prototype.cname = 'plant';
function tomato() {
function parentname() { return this.cname; }
return {
parentname:parentname
}
}
tomato.prototype = new plant(); // <-- settings it to plant as parent
var p = new plant();
var t = new tomato();
console.log(p.parentname()); //object
console.log(t.parentname()); //object
updated code - same result
Object.prototype.cname = 'object';
function plant() {
this.sayparentname = function() { console.log(cname); };
}
plant.prototype.cname = 'plant';
function tomato() {
this.sayparentname = function() { console.log(cname); };
}
tomato.prototype = new plant();
var p = new plant();
var t = new tomato();
p.sayparentname();
t.sayparentname();
Normally a constructor function will modify the object that new creates, with statements such as this.foo = bar, and not return anything. Then the result of the new expression is the object.
However, if the function returns an object, that object will replace the one that new created; so when you use new plant() you're just getting a plain Object instance back.
To fix your code you just need to make it like this:
function Plant() {
function parentName() { return this.cname; }
this.parentName = parentName;
}
I'd like to have an object with multiple levels of methods and properties. The top level will have properties and methods. Some of these properties will then act as name-spaces for second level methods and properties.
e.g.
//first level methods
base.doStuff();
base.doMore();
//second level methods
base.level2.doStuff();
Doing the first level is straight forward:
function Base(foo) {
this.foo = foo;
}
Base.prototype.doStuff = function () {
console.log(this.foo);
}
Base.prototype.doMore = function () {
console.log(this.foo);
}
base = new Base("bar");
base.doStuff();
Is it possible to get a second level, where in the function expression the "this" keyword points back to the Base constructor?
It's much easier to do this without prototypes:
function Base() {
var base = this;
base.level2 = {
moreStuff: function() {
// use "base" instead of "this" here
}
};
}
This can be combined with either prototypical methods, as in your example, or methods defined directly on base in the constructor. The downside of this is that you are creating the method functions every time you instantiate a new object, so you miss some of the shared-prototype goodness of standard prototypical methods.
You could create a new prototype-based object to be your level2:
function Level2() {}
Level2.prototype.moreStuff = function() {
// do stuff
}
function Base() {
this.level2 = new Level2();
}
But the methods of base.level2 won't be bound to base unless you bind them explicitly. Various libraries have bind support (e.g. Underscore's _.bind), or you can do it in plain JS:
function Base() {
var base = this;
base.level2 = new Level2();
base.level2.moreStuff = function() {
return Level2.prototype.moreStuff.apply(base, arguments);
}
}
You could further simplify here, but you're always going to have to make new methods bound in one way or another, because JS is never going to assign this in base.level2.moreStuff() to base without explicit binding - so in most cases the first option is the easiest and cleanest.
But really, is it worthwhile just for namespacing? If there's no functional value, it's a lot harder than simply calling your methods level2MoreStuff(), etc.
Well,
base.doStuff();
is calling doStuff in context of base. It is the same as
base.doStuff.call(base);
You can call and apply any function, for overriding this:
var base = new Base();
var someFun = function () {
console.log (this === base); // true
};
someFun.call(base);
Further anonymous example:
var anObj = {
method0: function () {
console.log (this === anObj); // true
}
};
anObj.method1 = function () {
console.log (this === anObj); // true
};
anObj.method0();
anObj.method1();
So the "second level" points this to level2, not to the "first level" object.
This is a really bad idea, but here goes:
function Base() {
this.name = 'Base';
this.level2 = new Level2(this);
}
Base.prototype.whatsMyName = function(){
alert(this.name);
};
function Level2(base) {
this.name='Level2';
for(var func in Level2.prototype) {
this[func] = Level2.prototype[func].bind(base);
}
}
Level2.prototype.whatsMyName = function(){
alert(this.name);
};
var b = new Base();
b.whatsMyName(); //Base
b.level2.whatsMyName(); //Also Base
You can see it running here: http://jsfiddle.net/zLFgd/1/
I'm trying todo some OO in Javascript, and I'm coming from C++ and Ruby. I've managed to create one object, but nesting an object inside is being alittle bit of a pain.
function Model()
{
//...
}
function Player(props)
{
var props = {
// ...
}
var model = new Model(props); // I've tried 'this.model = new Model() as well
}
var props = {
// ...
}
var player = new Player(props);
Player gets created fine, but if I try and nest the object it fails. What am I doing wrong.
Example
You were close. There are much better ways of "seudo-extending" object in javascript. jQuery.extend is one possible way. You can write your own method that check properties as well. I think the biggest break down for you was overwriting props in the Player function.
With functions this is key
Functions are the only scope in JavaScript, so be careful with naming variables
It's important to understand the difference between the object literal var a = {} and functions var a = new Method();. However, it seems you have that down well.
Code
function Model(data)
{
this.Name = data.Name;
this.Other = data.Other;
}
function Player(props)
{
var privateProps = {
Name: 'testing'
};
privateProps.Other = props.Other;
this.model = new Model(privateProps); // I've tried 'this.model = new Model() as well
}
var props = {
Other: 'Other'
}
var player = new Player(props);
I have this base type:
typeA = function () {
};
typeA.prototype = {
do = function() { alert ("do something"); },
doMore = function() { this.do(); }
}
and an inherited type typeB:
typeB = function () {
};
typeB .prototype = new typeA();
typeB.prototype.do = function() { alert ("do something else"); };
When I create an instance of typeB and call doMore, I get an error indicating that this.do is not a function. Can I do this sort of thing in Javascript?
Is this example what you are looking for?
typeA = function () { };
typeA.prototype = {
do : function() { alert ("do something"); }, //use : instead of = here
doMore : function() { this.do(); }
}
typeB = function () { };
typeB.prototype = new typeA();
typeB.prototype.do = function() { alert ("do something else"); };
var instance = new typeB();
instance.doMore();
You use : when declaring the properties of an object and = when assigning values to variables. :D
Additional explanation:
This is where the interesting stuff happens:
typeB.prototype = new typeA();
When you access a function or variable of an object with ., the browser first looks in the object itself to see if that variable is defined there. This is why you can do things like this:
var foo = function() {};
foo.prototype.bar = 3
instance = new foo();
alert( instance.bar ); //alerts 3
instance["bar"] = 55; //add a variable to the instance object itself
alert( instance.bar ); //alerts 55, instance variable masks prototype variable
This shows how there are two ways that something can be 'in' an object. It can either be in the object itself (which you can also do by adding this.bar = 55 to the constructor) or it can in the object's prototype.
Hence, when you say typeB.prototype = new typeA(); you are putting everything in that instance of typeA into typeB'prototype. What you've basically said is "Hey browser, if you can't find something in an instance of typeB, look to see if its in this instance of typeA!"
Turns out there's nothing actually in that instance, just things in its prototype that end up getting used when the browser can't find a variable of that name in that object itself. When you call instance.doMore(), the browser can't find it in instance, so it looks in typeB.prototype, which you just set to an instance of typeA. Since it can't find anything called doMore in that instance, it looks in its prototype, and finally finds a definition for doMore and happily calls it.
One interesting thing is that you can still mess around with things that are actually in that instance of typeA that you set to be the prototype:
//earlier code the same
foo = new typeA();
typeB.prototype = foo;
foo.do = function() { alert ("do something else"); };
//^^ same as `typeB.prototype.do = function() { alert ("do something else"); };`
var instance = new typeB();
instance.doMore();
While this is kind of cool when you understand what's going on IMHO, the extra layer of indirection (checking to see if stuff is defined in the instance of typeA before looking in typeA.prototype) is probably not the best idea, and your code would probably be clearer if you just said this:
typeB.prototype = typeA.prototype;
(sorry if you already knew everything I just told you, but I thought I'd describe how things were working under the hood ;)
You cannot use the word do because it is a reserved keyword (used in do while loop). You can, however, try this:
typeA.prototype = {
"do": function() { ... }
...
};
typeA["do"]();
It is better if you use constructor functions when working with prototypes.
What you have to do is instantiate the object be after you set its prototype of typeA. What you're doing is dynamically adding they new function of .do() to be after typeB is created. That is the only way you can do that.
function typeA() { };
typeA.prototype = {
'do': function () { alert("do something"); },
doMore: function () { this.do(); }
}
function typeB() { };
typeB.prototype = new typeA();
typeB.prototype['do'] = function () { alert('doing from typeB'); };
var b = new typeB();
//
b.do();
I'm not entirely sure how to implement OOP concepts in JS.
I have a class which is entirely declared in its constructor:
function AjaxList(settings)
{
// all these vars are of dubious necessity... could probably just use `settings` directly
var _jq_choice_selector = settings['choice_selector'];
var _jq_chosen_list = settings['chosen_list'];
var _cb_onRefresh = settings['on_refresh'];
var _url_all_choices = settings['url_choices'];
var _url_chosen = settings['url_chosen'];
var _url_delete_format = settings['url_delete_format'];
var jq_choice_selector_form = _jq_choice_selector.closest("form");
if (DEBUG && jq_choice_selector_form.length != 1)
{
throw("There was an error selecting the form for the choice selector.");
}
function refresh()
{
_updateChoicesSelector();
_updateChosenList();
_cb_onRefresh();
};
AjaxList.prototype.refresh = refresh; // will this be called on all AjaxLists, or just the instance used to call it?
// AjaxList.refresh = refresh; // will this be called on all AjaxLists, or just the instance used to call it?
// ...
}
There are multiple instances of AjaxList. When I call refresh() on one of them, I want only that one list to refresh itself. In the following instance:
term_list = AjaxList(settings);
term_list.refresh();
The refresh() call seems to make all the AjaxLists refresh themselves. What is the correct way to do this?
I'm using jQuery, if it makes any difference.
You should not redefine the prototype function in the constructor.
If you want to create a privileged function use this.methodname = ... from the constructor.
function AjaxList() {
var privateVar = 0;
function privateFunction() {
//...
}
//create a refresh function just for this instance of the AjaxList
this.refresh = function() {
//privileged function, it can access the 'privateVar & privateFunction'
privateVar++;
}
}
//public functions that don't need access to the private variables/functions
AjaxList.prototype.publicFunction=function() {
};
Also if you want to create a proper object, you need to change
term_list = AjaxList(settings);
to
term_list = new AjaxList(settings);
AjaxList = function(settings) {
this._jq_choice_selector = settings["choice_selector"];
this._jq_chosen_list = settings["chosen_list"];
this._cb_onRefresh = settings["on_refresh"];
this._url_all_choices = settings["url_choices"];
this._url_chosen = settings["url_chosen"];
this._url_delete_format = settings["url_delete_format"];
this.jq_choice_selector_form = _jq_choice_selector.closest("form");
if (DEBUG && jq_choice_selector_form.length != 1) {
throw "There was an error selecting the form for the choice selector.";
}
};
AjaxList.prototype = {
_updateChoicesSelector: function() { },
_updateChosenList: function() { },
_cb_onRefresh: function() { },
refresh: function() {
this._updateChoicesSelector();
this._updateChosenList();
this._cb_onRefresh();
}
};
Given that structure, you should be able to call:
var ajaxList = new AjaxList(settings);
ajaxList.refresh(); // etc.
I'm using jQuery, if it makes any
difference.
No it doesn't. See my answer here: What's the difference between Javascript, Jquery and Ajax?
I have a class which is entirely
declared in its constructor
There are no classes in Javascript. Forget them. You really need to learn some of the basics of this language in order to use them. It's not Java, even though it looks similar.
If you have a Constructor Function it will create an instance. The shared methods will be in the prototype chain, and only instance specific data goes right into the function with the this keyword.
So the basic concept of an object would look like this:
// constructor of an instance
function MyObject( param1, param2 ) {
this.param1 = param1;
this.param2 = param2;
this.param3 = 32;
return this; // [optional]
}
// Public methods can be called by any instance.
// Instances share their prototype object.
// The this keyword always points to the current
// instance that calls the method.
MyObject.prototype.sum = function() {
return this.param1 + this.param2 + this.param3;
}
// refresh should be a shared method, since it
// does the same thing on every instance
MyObject.prototype.refresh = function() {
// do the refresh
// ...
}
The power of this concept is that there is only one refresh function in memory. And it can deal with any instance. In addition, if another object inherits from MyObject the refresh function will be inherited. But in the memory there will be still one shared refresh function. And it can deal with any of the parent or child instances.