I'm trying to create a class in node.js for a small project of mine but I can't really figure out how scoping works.
I have a basic constructor function:
function testClass(username){
this.config = {
uName : username,
url : 'url_prefix'+username,
};
this.lastGame = {
firstTime : 1,
time : null,
outcome: null,
playingAs: null,
playingAgainst : null,
};
this.loadProfile(this.config['url']);
};
And the loadProfile function:
testClass.prototype.loadProfile = function(url){
request(url,function(error,response,body){
$ = cheerio.load(body);
matchTable = $('div[class=test]').children();
tempLast = matchTable.first().html();
if(this.config['firstTime'] == 1 || this.lastGame['time'] != tempLast){
this.lastGame['time'] = tempLast;
}
});
};
(I'm using the Request and Cheerio libraries.)
The problem I have is that I can't use the class variables using "this" inside the "request" function.
It returns "Cannot read property 'firstTime' of Undefined".
This only happens inside the "request" function. I can use "this" and all its functions/variables just fine outside it.
I've thought about passing it to the function but a) I couldn't find how and b) That would mean that any modification I made to the variables wouldn't change the actual class variables.
Could anyone please explain what is going on?
Thanks a lot!
The typical solution is to copy this into another variable called self.
However, if you aren't going to be creating very many instances of your "class", or it only has a few methods, then it's generally simpler to avoid using constructor functions, this and new altogether.
function makeAnObject(username){
// declare private information:
var url = 'url_prefix' + username;
// return public information (usually functions):
return {
loadProfile: function(blah) {
// ...
}
};
};
This lets you have genuinely private data, and you don't have to copy the parameters of makeOnObject by hand, and you don't have to worry about this being broken, or remember to prefix calls with new, etc.
Every function creates a new scope, since scopes are function-centric in JavaScript. The ES6 let keyword will help you circumvent this kind of scenario. Before that, you'll have to stick to retaining a reference to the this you mean to use.
testClass.prototype.loadProfile = function(url){
var self = this;
request(url,function(error,response,body){
$ = cheerio.load(body);
matchTable = $('div[class=test]').children();
tempLast = matchTable.first().html();
if(self.config['firstTime'] == 1 || self.lastGame['time'] != tempLast){
self.lastGame['time'] = tempLast;
}
});
};
Update
if I set self.config['time'] = "whatever", this.config['time'] remains unchanged.
Yes. That's because this refers to the request function local scope, rather than the loadProfile scope you want to refer to. That is why you should be using the self reference, rather than this. self kept a reference to this in the context of loadProfile. Then, this changed when you entered the request callback's context.
Inside request you have different scope. This inside request function is probably instance of request object. You could try something like:
testClass.prototype.loadProfile = function(url){
var self = this;
request(url,function(error,response,body){
$ = cheerio.load(body);
matchTable = $('div[class=test]').children();
tempLast = matchTable.first().html();
if(self.config['firstTime'] == 1 || self.lastGame['time'] != tempLast){
self.lastGame['time'] = tempLast;
}
});
};
Related
Everything is in the the title really... I know that functions created using prototype can't have access to private object data/functions, but what about having access to the arguments that were passed to the object when it was created ?
var Voice = function (word)
{
/*
I know I can obviously do something like : 'this.word = word;'
But I was wondering whether there is a standard way of calling an
argument from within a prototype function without having to do
the above ?
*/
};
Voice.prototype.speak = function ()
{
console.log({{word}});
};
x = new Voice('all I can say is this');
x.speak();
Thanks!
No.
The functions on the prototype weren't defined within the function that the variables are in scope for, so they don't have access to them.
You can store the variable as an object property and then read it back from there.
this.word = word;
Maybe like this:
var Voice = function (word) {
this.init_word = word;
};
Voice.prototype.speak = function (){
console.log(this.init_word);
};
x = new Voice('all I can say is this');
x.speak();
Just made the switch over to ES6, running io.js.
I'm writing some class code, but I'm having an unexpected error.
'use strict';
var _ = require('lodash');
class Country {
constructor(blocked) {
this.blocked = ['USA'];
}
ok(input) {
console.log('Receiving...',input['country']);
console.log('Blocked:', this.blocked);
if(_.includes('USA', input['country'])) {
return -1;
}
return 0;
}
}
module.exports = Country;
For whatever reason, everything works well except for the this.blocked class variable.
When I console log it, it shows up as Blocked: undefined.
Any thoughts as to what's going on here?
Addition
I'm calling the function in another class as follows...
var Country = require('./filters/country.js');
var country = new Country();
class FilterClassifier {
constructor() {
var self = this;
self.filters = [country.ok];
}
userFilter(params) {
var self = this;
var input = {
country : params.country,
};
console.log(self.filters[0](input));
}
}
module.exports = FilterClassifier;
As mentioned in the comments, the way you are calling the function removed the context of the function.
self.filters = [country.ok];
and then
console.log(self.filters[0](input));
means that this inside ok will not be country. You'll need to do
self.filters = [country.ok.bind(country)];
or
self.filters = [() => country.ok()];
I'd recommend reading up on this in javascript. The short answer in this particular case is that this is defined based on how a function is called.
var a = {};
a.fn = function(){};
var b = {};
b.fn = a.fn;
b.fn();
When calling b.fn(), this inside the function is b. This is because when calling a function using the form foo.bar(), this inside a function is defined as the object that the function is called on (foo). In your case, you have
self.filters[0]();
that means that this inside your ok functions is actually self.filters for the same reason.
If you have a specific this that matters, it is your responsibility to make sure that as you pass around a function, that the function you are passing will set the proper this. Using fn.bind(foo) will return a new function which, when called, call fn with a given this.
It looks like this.blocked is scoped to the constructor function, this in ok doesn't have a identifier called blocked.
Edit: I was wrong, thanks Pointy. In the interest of helping others who stumble on this post I'm not going to delete my wrong answer but instead share what I learned. David, in the comments you asked for a way to fix it. this could be replaced with let variables to avoid confusion or use this with bind() when a function is called.
class Country {
// variable scoped to class with let
// set blocked = value in constructor
// should not need 'this.' nor 'bind()'
let blocked = [];
...
}
"Determining this" says you had default this binding: undefined. You hoped for implicit binding and got explicit binding to work with country.ok.bind(country).
Further reading illustrating how the fat arrow => and var self = this; make this confusing.
Why does the marked line fail to find protectedACMember?
var Module = (function (ns) {
function AbstractClass() {
this.protectedACMember = "abstract";
this.abstractPublicACMethod = function (input) {
this.methodToImplement();
}
}
ConcreteClass.prototype = new AbstractClass();
function ConcreteClass(){
var privateCCMember = "private CC";
var privateCCMethod = function(){
alert(this.protectedACMember); // cant find protectedACMember
}
this.methodToImplement = function(){
privateCCMethod();
console.log('Implemented method ');
}
}
ns.ConcreteClass = ConcreteClass;
return ns;
})(Module || {});
//somewhere later
var cc = new Module.ConcreteClass();
cc.abstractPublicACMethod();
are there any good patterns for simulating private, protected and public members? Static/non-static as well?
You should change that part of code like this:
var self = this;
var privateCCMethod = function(){
alert(self.protectedACMember); // this -> self
}
This way you get the reference in the closure.
The reason is, that "this" is a reserved word, and its value is set by the interpreter. Your privateCCMethod is an anonymous function, not the object property, so if you call it simply by privateCCMethod() syntax, this will be null.
If you'd like "this" to be bound to something specific you can always use .call syntax, like this:
privateCCMethod.call(this)
Another way to ensure that this means what you want is to use bind. Bind allows you to ensure a function is called with a specific value of this.
Most newer browsers support it (even IE9!) and there's a workaround for those that don't.
Bind - MDN Documentation
It fails to find protectedACMember because what the this keyword means changes when you enter the function privateCCMethod. A common practice is to store the outer this for use inside the functions:
function ConcreteClass(){
var privateCCMember = "private CC";
// store the outer this
var that = this;
var privateCCMethod = function(){
alert(that.protectedACMember);
}
...
The rest of your questions are fairly loaded and should probably be posted as a separate question.
This question is simplified version of my old question Adding scope variable to a constructor. Question is simple can I add priv variable to the fu()'s scope without changing the function? (not adding inside of the function block)
Here is fiddle
Here is the code:
fff = function() {
alert('constructed');
//alert(priv);
};
pro = {
pub: 'public'
}
var make = function(fu, pro) {
var priv = 'private';
fu.prototype = pro
return function() {
return new fu();
};
};
var cls = make(fff, pro);
var obj = cls();
alert(obj.pub);
As you can see if you de-comment the
//alert(priv);
line Uncaught ReferenceError: priv is not defined error.
I need a way to redifine the scope of the fu() function object.
I don't see the fu object listed, but I think the answer is "yes", you can add a private variable without changing the "function". Now, I may be missing something, but if I follow you, here is what you want:
var fu = {
DoStuff: function(someVar){
alert(someVar);
}
};
Then later in your code:
fu["NewPrivateVar"] = "something!";
Or in dot notation:
fu.NewPrivateVar = "someting!";
Finally:
fu.DoStuff(fu.NewPrivateVar);
Results in:
"something!"
Is that what you are looking to do?
You can't change the scope of the function by calling it from inside an object or a closure.
You can however add the variable to the scope of the function, i.e. in the global scope:
window.priv = 'private';
That will make the function work without changes, but the variable isn't very private...
When I try and make different instances of this module, it does not work.
It seems to be a singleton. I can only have one instance at a time.
What mechanism limits the constructor function publik() to only have on instance?
http://jsfiddle.net/AVxZR/
var Module = ( function ()
{
var publik = function ( )
{
};
publik.prototype.test;
publik.prototype.get = function()
{
document.getElementById( 'a'+test ).innerHTML = test;
};
publik.prototype.set = function( value )
{
test = value;
};
return publik;
} ) ();
var object1 = new Module();
var object2 = new Module();
object1.set('1');
object2.set('2');
object1.get();
object2.get();
The module pattern is not meant to be used in the manner you've described. It's used to create one module and hide state from outside code, i.e. you expose one public interface with which outside code can communicate but you keep the rest hidden.
This prevents other code from relying on variables or functions you are using internally, as they would break when you rename anything.
Also, a module is supposed to be singleton; to have multiple identical modules is like having two identical classes in your code ... doesn't make sense.
This is how a module pattern should look like.
var Module = (function($) {
// the $ symbol is an imported alias
// private variable
var id = 0;
// private function
function increaseId()
{
return ++id;
}
// return public interface
return {
nextId: function() {
// we have access to the private function here
// as well as the private variable (btw)
return increaseId();
}
}
}(jQuery)); // we import jQuery as a global symbol
Module.nextId(); // 1
Module.nextId(); // 2
Module.id; // undefined
Module.increaseId(); // error
You see how only .nextId() is exposed, but none of the other private variables / functions.
The short answer: closure.
The long answer (if I have it right, please comment so I can correct):
Your Module var is a executed immediately when the script loads. (denoted by the parenthesis around the function.)()
In that module, your publik var is declared and it's left in the closure even when the function completes!
With subsequent calls, you still access that one Module that was auto-executed. And it always gets that same closure space and function scope and the same object, in short - so your publik variable is actually always the same one.
Try rewriting the Module class so that you can use it to create different instances. You may want to alter the "test" property to be a static property as I have changed it for you.
var Module = function(){}
Module.prototype.test;
Module.prototype.get = function()
{
document.getElementById( 'a'+this.test ).innerHTML = this.test;
};
Module.prototype.set = function( value )
{
this.test = value;
}
You code doesn't create a singleton. It only acts like a singleton since your test variable is a global variable.
To fix this change test to this.test so the variable is attached to each instance.