Can Javascript objects call their own methods during construction? - javascript

I have the following JS, interfacing with the Undum interactive fiction library:
undum.game.situations = {
main: new undum.SimpleSituation(
"",
{
enter: function(character, system, from) {
...
},
actions: {
testMethod: function() {
return "hello from test method";
},
'test-action': function(character, system, action) {
console.log("testMethod for main situation says: "+this.testMethod());
...
}
}
}
),
...other situation defs
}
when I get into the test-action method of actions, the console shows the error "Uncaught TypeError: this.testMethod is not a function".
I'm guessing the problem is related to the fact that testMethod and its caller are defined as part of the SimpleSituation constructor? If so, how should I go about setting up event-based code like this where an object needs to hook into its own methods during construction (although they aren't actually called during construction)?
A fiddle with some similar boilerplate that I think demos the same categorical behavior is working fine:
function Obj(optionsObj) {
optionsObj.testMethod();
}
var myObj = new Obj(
{
testMethod: function() {
alert("hello from test method");
}
}
)

The key to understanding this problem was that in JS keyword this is all about the calling context rather than the called context -- I had read the W3 schools blurb about keyword this, but it's kind of misleading. In their example code
var person = {
firstName: "John",
lastName : "Doe",
id : 5566,
fullName : function() {
return this.firstName + " " + this.lastName;
}
};
they call out fullName as a method of person, in which keyword this will refer to the enclosing object. That's only true, however, if the calling context of fullName is on an instance of person, e.g.
var p = new person();
p.fullName();
Being called on an instance of person is what makes fullName a method. On its own, fullName is just a function like any other, however, and can be extracted from person and used as a standalone function:
var fullNameFn = person.fullName;
fullNameFn(); // in this case, keyword this points to global object (usually Window) or undefined in strict mode.
The situation I was looking at in Undum turned out to be such a case. Instead of calling main.actions.test-action(), it was doing the following in response to player clicking an action link:
SimpleSituation.prototype.act = function(character, system, action) {
// my take-action action function is extracted and stored as response
var response = this.actions[action];
try {
response(character, system, action);
} catch (err) {
if (response) system.write(response);
}
if (this._act) this._act(character, system, action);
};
the actual test-action function is extracted from the Situation instance and executed standalone, causing keyword this to point to Window.

Related

JS OOP outside prototype function call ( scope )

I'm new to OOP, and I'm writing a simple game script to learn the OOP principles.
//main game function
battleLevel.prototype =
{
battle:function () {
this.obj1 = {
enterMonsterMenu: function() {
return console.log('enterMonsterMenu');
}
};
},
} /* end OOP prototype */
//external contructor
var Hero = function (warpX, warpY, game, name, life, mana, speed) {
//some code
};
Hero.prototype.monsterSelectUp = function() {
console.log('monsterSelectUp');
//this.enterMonsterMenu();
battleLevel.prototype.battle.call(obj1);
};
I want to access enterMonsterMenu() method by calling the monsterSelectUp() but I can't call it properly. What I am doing wrong ?
It looks like you didn't get the concepts right, try to re-read at least this short intro.
Let's try to look what happens in the line where you are trying to call "enterMonsterMenu".
Here it is:
battleLevel.prototype.battle.call(obj1);
The battleLevel.prototype is an object you defined first.
The battleLevel.prototype.battle is a function and you execute it's "call" method (because functions are also objects in js and have functions like "call").
What does "function.call" method? It calls the function with given this value.
For example,
var myObject = { name: "Object 1" };
var yourObject = { name: "Object 2" };
function test() {
alert(this.name);
}
test.call(myObject); //alert Object 1
test.call(yourObject); //alert Object 2
In your code, you are trying to call the battleLevel.prototype.battle and passing the obj1 as this.
But at that point of code there is no obj1 variable defined, so you just call the battle method with undefined variable.
Moreover, even if you passed the defined variable, you would not call the enterMonsterMenu function anyway. Because your method only adds the obj1 property to the this object:
battleLevel = {}
battleLevel.prototype =
{
battle:function () {
this.obj1 = {
enterMonsterMenu: function() {
alert('enterMonsterMenu');
}
};
},
}
var myThis = {"property": "value"};
alert(JSON.stringify(myThis)); // {"property": "value"};
// This call will add "obj1" property to myThis
battleLevel.prototype.battle.call(myThis);
alert(JSON.stringify(myThis)); // {"property": "value", "obj1": {}};
// now call the 'enterMonsterMenu'
myThis.obj1.enterMonsterMenu();
You can see above how you can actually call your enterMonsterMenu, but to be honest, I see no point in doing things like this. And, as I said, you probably need to spend more time on learning the concepts.

Prototype function overriden by other "class"

I have the following inputModel:
var UserCreateInputModel = function(req) {
...
this.password = req.body.password;
this.repeatPassword = req.body.repeatPassword;
...
console.log(this.password !== this.repeatPassword);
this.validate();
};
UserCreateInputModel.prototype = InputModel;
UserCreateInputModel.prototype.validate = function() {
console.log('Validating...');
if(this.password !== this.repeatPassword) throw new Error('Passwords are not equal!');
};
module.exports = UserCreateInputModel;
In my test I would like to test if the exception was thrown (using node's assert module):
//Act
assert.throws(function() {
new UserCreateInputModel(req);
}, Error);
Somehow the exception is just not thrown. My console output from the constructor is "this".
On the console I don't see the output "Validating".
I guess this is some JavaScript pitfall or something like that (about this) but I'm just not getting the error ...
Update
I have another inputModel in another file. This "inherites" from InputModel too. So it seems UserCreateInputModel.prototype.validate is overridden in the other inputModel. Still don't get it ...
This line:
UserCreateInputModel.prototype.validate = function() {
console.log('Validating...');
if(this.password !== this.repeatPassword) throw new Error('Passwords are not equal!');
};
is modifying the InputModel object. If you have two different types "inheriting" from InputModel with what you have here, and they both have validate methods, then one is overwriting the other.
To properly inherit from InputModel, use Object.create():
UserCreateInputModel.prototype = Object.create(InputModel.prototype);
And then call the parent constructor from your child constructor:
var UserCreateInputModel = function(req) {
InputModel.call(this, <arguments>);
This way, when you modify UserCreateInputModel.prototype, you will only be modifying UserCreateInputModel.prototype and not anything else.
UserCreateInputModel.prototype = InputModel is not how you do inheritance. Who taught you this???!!!
It's:
after the constructor, util.inherits(UserCreateInputModel, InputModel) (where util is require('util')), and
inside the constructor, calling InputModel.apply(this, arguments); or InputModel.call(this, <explicit list of input model ctr args>).
Get back with another EDIT if it still persists.

Pass a Javascript Class in MongoDB MapReduce scope to be used by map

I wrote a class Student in Javascript.
function Student(info) {
this.getName(info);
this.getAge(info);
}
Student.prototype.getName = function(info) {
this.name = info.name;
};
Student.prototype.getAge = function(info) {
this.age = info.age;
};
Now, I need this class inside the map function of mongoDB mapReduce framework. That is,
var mapFunction = function() {
var student = new Student(this);
emit(student.name, student.age);
};
This function map doesn't have access to Student defined outside this function. Therefore, I need to pass this class through scope of mapReduce.
var scopeVar = { Student: Student};
db.collection.mapReduce(
mapFunction,
{
scope: scopeVar,
out: { merge: 'testCollection'}
}
);
However, turns out that inside map, we have Student defined but the Student.prototype is empty. To test this I wrote alternative mapTest,
var mapTest = function() {
emit(Student, Student.prototype);
};
var scopeVar = { Student: Student};
db.collection.mapReduce(
mapTest,
{
scope: scopeVar,
out: { merge: 'testCollection'}
}
);
In db.testCollection, one can see that output document looks like this
{_id: function Student(info) {
this.getName(info);
this.getAge(info);
},
value: {}
}
Therefore, it seems that somehow scope doesn't copy the prototype of the object.
If one wants to define helper functions as prototype function of the class, how can one pass it through the scope of mapReduce?
My hypothesis is that MongoDB is implemented in C, the CLI or execution engine reads the code and submits it to the V8Engine. So, the interpreted context of Prototype is not perceived by the CLI and hence is not submitted to V8 Engine. The scope paramater enhances the parameter mechanism, but does not give the full dynamic nature as expected. Internally, mongodb must be creating another function with given scope. To achieve what you mentioned, I would try something like this:
This should work.
var scopeVar = { Student: Student, StudentPrototype: Student.prototype };
var mapFunction = function() {
Student.prototype = StudentPrototype;
var student = new Student(this);
emit(student.name, student.age);
};
Above answer is correct in the approach. The correct answer is following.
var scopeVar = { Student: Student, StudentPrototype: Student.prototype };
var mapFunction = function() {
Student.prototype = StudentPrototype;
var student = new Student(this);
emit(student.name, student.age);
};

What's the best way to create JavaScript classes? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Use of ‘prototype’ vs. ‘this’ in Javascript?
Object Oriented questions in Javascript
Can you please recommend which of the following is the best or their pros and cons?
Method 1
function User() {
this.name = "my name";
this.save = function() {
};
}
Method 2
function User() {
this.name = "my name";
}
User.prototype.save = function() {
}
Coming from a background in Java and PHP, I had initially struggled with the whole 'class' concept in JavaScript. Here are a few things to think about.
Construction
First, since you're likely going to be defining your classes as..
Customer = function () {}
Customer.prototype = new Person();
You will end up running into all sorts of nightmares of code if you are defining properties and methods during construction. The reason being is that a piece of code like Customer.prototype = new Person(); requires that the Person be called in the Customer constructor for true inheritance.. otherwise you'll end up having to know what the original one sets at all times. Take the following example:
Person = function (name) {
this.name = name;
this.getName = function () {return this.name}
}
Customer = function (name) {
this.name = name;
}
Customer.prototype = new Person();
Now we're going to update Person to also set whether they are 'new':
Person = function (name, isNew) {
this.name = name;
this.isNew = isNew;
this.getName = function () {return (this.isNew ? "New " : "") + this.name; }
}
Now on any 'class' that is inheriting from the Person, you must update the constructor to follow form. You can get around that by doing something like:
Customer = function () {
Person.apply(this, arguments);
}
That will call 'Person' in the scope of the new 'Customer', allowing you to not have to know about the Person construction.
Speed
Take a look at these benchmarks: http://jsperf.com/inherited-vs-assigned.
Basically, what I am attempting to prove here is that if you're creating these objects en masse, your best route is to create them on the prototype. Creating them like:
Person = function (name) {
this.name = name;
this.getName = function () {return this.name}
}
is very slow, because for every object creation, it creates a new function - it doesn't simply look up the prototype chain for the already existing one. Conversely, if you've got only a few objects that the methods are called extremely frequently on, defining them locally helps with the speed lost by looking up the prototype chain.
Shared properties
This always gets me. Let's say you've got something like the following:
Person = function () {
this.jobs = {};
this.setJob = function (jobTitle, active) {this.jobs[jobTitle] = active; }
}
Employee = function () {}
Employee.prototype = new Person();
var bob = new Employee();
bob.setJob('janitor', true);
var jane = new Employee();
console.log(jane.jobs);
Guess what? Jane's a janitor! No joke! Here's why. Since you didn't define this.jobs as being a new object on instantiation of the Employee, it's now just looking up the prototype chain until it finds 'jobs' and is using it as is. Fun, right?
So, this is useful if you want to keep track of instances, but for the most part you're going to find it incredibly frustrating. You can get around that by doing the following:
Employee = function () { Person.apply(this); }
This forces 'Person' to create a new 'this.jobs'.
Private variables
Since there's nothing that's really "private" in JavaScript, the only way you can get private variables is to create them in the constructor, and then make anything that relies on them initialize in the constructor.
Person = function () {
var jobs = {};
this.setJob = function (jobTitle, active) {jobs[jobTitle] = active; }
this.getJob = function (jobTitle) { return jobs[jobTitle]; }
}
However, this also means that you must call that constructor on every instantiation of an inherited class.
Suggestion
http://ejohn.org/blog/simple-javascript-inheritance/
This is a super basic class setup. It's easy. It works. And it'll do what you need it to do 90% of the time without having to deal with all the insanity JavaScript has to offer :)
</rant>
Best is a very subjective term.
Method 1 gives the possibility of truly private variables. e.g :
function User() {
var name = "fred";
this.getName = function () { return name;};
}
Method 2 will use less memory, as a single save function is shared by all User objects.
'Best' will be determined by your specific requirements.
JavaScript is an "Object Based" language, not a "Class Based" language. Shared behaviours, which is the class concept, are implemented by linking prototypes.
Method 1 does NOT create a class. Every time you invoke it, you create attributes and functions as defined and they are NOT shared with other "instances". If you replace this.save then only the one instance will have altered behaviour.
Method 2 implements shared behaviours. Therefore it is what people associate with classes and instances. If you replace this.save withe a different function, then all derived instances will immediately have the new behaviour.
If "best" means no memory consuming redefinitions of functions and sharing common behaviours (which is what class based programming tends to equate to) the Method 2 is the way to go.
As others have mentioned, there are two main differences:
Method 1 will create a new function for every instance of User
Method 1 will be able to access local variables in the constructor's scope
Here's an example to demonstrate these:
function User() {
var name = "my name";
this.foo = function() {
// one function per User instance
// can access 'name' variable
};
}
User.prototype.bar = function() {
// one function shared by all User instances
// cannot access 'name' variable
};
var a = new User();
var b = new User();
console.log(a.foo === b.foo); // false; each instance has its own foo()
console.log(a.bar === b.bar); // true; both instances use the same bar()

Structuring a NodeJS module - variables and methods

I want to create modules to structure my NodeJS application, but I'm a little lost, and I haven't found anything (with hours of searching) that is completely definitive on the subject.
Say I'd like to create a "user" module, from which I can create new users in my code using something like:
var newUser = new User();
Ideally, I'd require my module at the top of my code using something like:
var User = require('../lib/user');
This works great. The question is, how should I structure the user module? Is the following the best way?
module.exports = function User() {
var authorized = false;
var username = undefined;
var password = undefined;
var statistics = undefined;
this.authorized = function() {
return authorized;
}
this.username = function() {
return username;
}
this.statistics = function() {
return statistics;
}
}
I'm writing getters and setters for my various module variables, allowing me to hide things I don't want to accidentally access from other code. However, I have done it this way before:
function User() {
this.authStatus = false;
this.email;
this.displayName;
this.inSession;
}
User.prototype.isAuthenticated = function() {
return(this.authStatus && this.email && this.displayName)
}
User.prototype.isInSession = function() {
return(this.inSession && this.isAuthenticated());
}
exports.User = User;
This works too, with one caveat; I haven't found a way to access the user properties from within closures. If my understanding is correct, with the second implementation, I can't. This means if I need to hand a function off to a db library as a callback to edit the user's properties, I can't. That'd look something like:
User.prototype.login = function() {
db.doDbStuff('get user data query', function(_error, _result) {
this.username = _result[0].name; //this code will not work
});
}
The code doesn't work, to my understanding, because the "this" keyword is within the scope of the closure, not the User. Even if the code were to be placed within the User function:
function User() {
this.login = function() { //you know
It wouldn't work.
I guess my question is, what's the best solution to this problem? Is it the method I presented in the first code block? That seems rather cumbersome and messy and prone to variable collision. I'm scared.
Thanks in advance!
I typically go with the second approach, attaching functions to the prototypes.
The issue you're having with variables "not being available in closures" has nothing to do with prototypes. You'd have that same issue either way you structure it.
It's to do with javascript's oft-confusing dynamic this:
http://robotlolita.me/2011/10/09/understanding-javascript-oop.html#sec-2-1
Basically, you need to do something like:
User.prototype.login = function() {
var self = this // bind this to self so you have a reference to what `this` was
db.doDbStuff('get user data query', function(_error, _result) {
self.username = _result[0].name; // self refers to the original `this`, so this works.
});
}
You also have the option of using function.bind: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
Within the bound function, the value of this will be whatever value you provided to .bind(value):
User.prototype.login = function() {
db.doDbStuff('get user data query', (function(_error, _result) {
this.username = _result[0].name; // bind fixes the original `this`, so this also works.
}).bind(this));
}
Whether you use function.bind or self = this is somewhat of a personal taste question, but we were doing some benchmarks in the freenode#nodejs the other day and discovered bind() is 20 something times slower than var self = this.
As to your original question about how to structure modules, there are so many examples to learn from on github. Simply find your favourite module and inspect how they structure it. I notice that many people seem to prefer factories over exposing constructors directly (e.g. require('module').create() ). Your call.
As a different approach, I am a fan of the following pattern.
module.exports = function User(data) {
//this properly private stuff.
var privateVar;
var username = data.username;
var pass = data.pass; //etc
function privateFunc() {
}
return {
login: function(cb) {
db.doStuff(username, pass, cb);
}
};
};
User.prototype.login = function() {
var _this = this;
db.doDbStuff('get user data query', function(_error, _result) {
_this.username = _result[0].name; //this code will now work
});
}
The 'this' you used was outside its scope, it was the callback 'this'.

Categories

Resources