Instance methods not in scope of JS module in NodeJS? - javascript

I'm trying to create a non-singleton object in NodeJS but when calling internal instance methods within the object, they are "undefined".
// We auto-refresh the cached data every 'refreshInterval' milliseconds
var refreshInterval = 30*1000;
var refreshTimer = null;
// We stop refreshing 'idleTimeout' milliseconds after the last request for our data
var idleTimeout = 5 * 60 * 1000; // 5 minutes
var idleTimer = null;
var cachedData = null;
var queryCallback = null;
function Cache(options) {
if (options) {
this.refreshInterval = options.refreshInterval || 30;
this.queryCallback = options.queryCallback || null;
this.idleTimeout = options.idleTimeout || 5 * 60 * 1000;
}
}
Cache.prototype.data = function(callback) {
updateIdle();
if (cachedData) {
callback(cachedData);
} else {
queryData(function(newdata) {
callback(newdata);
});
}
}
Cache.prototype.updateIdle = function() {
idleTimer && clearTimeout(idleTimer);
idleTimer = setTimeout(haltRefresh, idleTimeout);
wslog.info(`Will cease cache refresh at ${new Date(Date.now() + idleTimeout)} if no new activity`);
}
// ...
module.exports = Cache;
When I create the object
var Cache = require('./cache.js');
var myCache = Cache(opts);
It works but when I invoke myCache.data(), the invocation of the internal method updateIdle() halts the app because ReferenceError: updateIdle is not defined
Something tells me NONE of my instance methods are going to be "defined" even though they plainly are. And before I made them prototype methods, they worked fine. These would be a valid Java instance method invocations - is there some scope issue with JS?
Should I remove the Cache.prototype. from the functions I don't need exposed and then just use them? Do I have to add this. to any instance method invocations?
What do I need to know to make JS "just work" like normal OOP?

Related

ReferenceError: count_1 is not defined in a Joomla module

This is my script:
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var START_DATE_1 = new Date("July 18, 2016 10:30:00"); // put in the starting date here
var INTERVAL_1 = 3; // in seconds
var INCREMENT_1 = 1; // increase per tick
var START_VALUE_1 = 0; // initial value when it's the start date
var count_1 = 0;
var msInterval_1 = INTERVAL_1 * 1000;
var now_1 = new Date();
count_1 = parseInt((now_1 - START_DATE_1)/msInterval_1) * INCREMENT_1 + START_VALUE_1;
document.getElementById('counter_1').innerHTML = count_1;
setInterval("count_1 += INCREMENT_1; document.getElementById('counter_1').innerHTML = count_1;", msInterval_1);
});
</script>
I've placed it inside a Joomla module. Firebug says: "ReferenceError: count_1 is not defined"
Why? How can I solve it?
You should pass a function instead of a string to the setInterval call. When you pass a string, it is executed in the global scope, and count_1 variable exists only in the scope of the callback function passed to addEventListener.
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var START_DATE_1 = new Date("July 18, 2016 10:30:00"); // put in the starting date here
var INTERVAL_1 = 3; // in seconds
var INCREMENT_1 = 1; // increase per tick
var START_VALUE_1 = 0; // initial value when it's the start date
var count_1 = 0;
var msInterval_1 = INTERVAL_1 * 1000;
var now_1 = new Date();
count_1 = parseInt((now_1 - START_DATE_1)/msInterval_1) * INCREMENT_1 + START_VALUE_1;
document.getElementById('counter_1').innerHTML = count_1;
setInterval(function() {
count_1 += INCREMENT_1;
document.getElementById('counter_1').innerHTML = count_1;
}, msInterval_1);
});
</script>
When you pass in a string as the first argument to the timers function, they actually create a script object with that as source:
[Otherwise]
Perform HostEnsureCanCompileStrings(callerRealm, calleeRealm). If this throws an exception, report the exception.
Let script source be the first method argument.
Let settings object be method context's environment settings object.
Let script be the result of creating a classic script using script source and settings object.
Run the classic script script.
https://html.spec.whatwg.org/multipage/webappapis.html#dom-setinterval
So, it's not exactly as eval where you actually get access to the scope of the function. The script object should have access to the global scope. The reason it fails is because is because it looks for count_1 on the global scope through count_1 += INCREMENT which isn't find and therefore returns the ReferenceError.
Best way to avoid this type of errors is to not use a string as an argument for the timers function.

JSLint unexpected 'that' message

I've just started using JSLint to make sure that the JavaScript code I'm creating at least meets some standards and I've got a confusing message:
JSLint: Unexpected 'that'.
The code is part of a solution to implement a progress bar, part of which is this object that handles timers and callbacks and is as follows (this is an extract from the beginning of a larger file, I can add the whole file if required):
var ProgressHandler = function () {
"use strict";
// Build a new object
var that = {};
// Add basic properties
that.taskid = 0;
that.timerid = 0; // Timer ID used to push refreshes
that.progressUrl = ""; // URL to invoke to read progress
that.interval = 500; // The interval for progress refresh
that.taskProgressCallback = null; // The user-defined callback that refreshes the UI
that.taskCompletedCallback = null; // The user-defined callback that finalizes the call
// Set progress url
that.setProgressUrl = function (url) {
that.progressUrl = url;
return this;
}
// Set frequency of refresh
that.setInterval = function (interval) {
that.interval = interval;
return this;
};
The message appears on the line that begins that.setInterval. There are further uses of that I but JSLint also says that it stops processing at this point. I've tried searching for this message but it's not listed specifically here or on jslinterrors.com.
Why is this appearing and what can I do to fix it? Or should it just be ignored?
The issue seems to be that you do not have a ; after the definition of that.setProgressUrl. Changing to:
// Set progress url
that.setProgressUrl = function (url) {
that.progressUrl = url;
return this;
};
Resolves the reported issue. You then have an issue in that you are missing a closing brace and semi colon at the end of the file, not sure if that is just a copy and paste issue. The complete script should look like:
var ProgressHandler = function () {
"use strict";
// Build a new object
var that = {};
// Add basic properties
that.taskid = 0;
that.timerid = 0; // Timer ID used to push refreshes
that.progressUrl = ""; // URL to invoke to read progress
that.interval = 500; // The interval for progress refresh
that.taskProgressCallback = null; // The user-defined callback that refreshes the UI
that.taskCompletedCallback = null; // The user-defined callback that finalizes the call
// Set progress url
that.setProgressUrl = function (url) {
that.progressUrl = url;
return this;
};
// Set frequency of refresh
that.setInterval = function (interval) {
that.interval = interval;
return this;
};
};

D3 dispatch pass in arguments and calling context

I understand that in D3, dispatch can be used to fire events to multiple visualisations according to this example.
I also understand that if I want to call a dispatch from an object and pass in the context, I can use apply as shown here.
However, I'm having a hard time combining the arguments from a D3 dispatch and the context that I want.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse);
// this next line isn't working, since the
// console.log in probeLoad still returns undefined
probeDispatch.probeLoad.apply(this);
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
new genChart("pants");
new genChart("shirt");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
It does set the context properly when you see "pants" in the console.
But then there are 3 undefined's logged, because you also call
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
without supplying context.
You need
probeDispatch.probeLoad.apply(instanceOfGenChart, [lines[line_count][0], lines[line_count][1]]);
But enabling that also requires moveing parse_dispatch down the page.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse);
// this next line isn't working, but I don't know what to do
probeDispatch.probeLoad.apply(this);
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
var instanceOfGenChart = new genChart("pants");
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad.apply(instanceOfGenChart, [lines[line_count][0], lines[line_count][1]]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
So it turns out to bring the context into the function, I have to bind() it for reasons I'm not too clear on.
// create my dispatcher
var probeDispatch = d3.dispatch("probeLoad");
var line_count = 0;
// load a file with a bunch of JSON and send one entry every 50 ms
var lines = [[0,1],[1,2],[2,0]];
var parse_timer = window.setInterval(
function () {
parse_dispatch();
}, 50
);
function parse_dispatch(){
// send two arguments with my dispatch
probeDispatch.probeLoad(lines[line_count][0], lines[line_count][1]);
line_count += 1;
if(line_count >= lines.length){
//line_count = 0
window.clearInterval(parse_timer);
}
}
// my chart object
var genChart = function(label){
this.label = label;
// assume I've drawn my chart somewhere here
probeDispatch.on(("probeLoad."+this.label), this.probeParse.bind(this));
};
genChart.prototype = {
probeParse: function(probeData, simTime) {
// How do I get the context from the object that's calling probeParse
// into the probeParse scope?
var self = this;
console.log(self.label);
}
};
new genChart("pants");
new genChart("shirt");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Added by meetamit
Bind is the solution here, because it locks a scope to an "instance" of genChart.prototype. probeParse. This way parse_dispatch (the invoker) doesn't need to know anything about scope. It's equivalent to this:
// my chart object
var genChart = function(label){
this.label = label;
var self = this;
var probeParseBound = function() { self.probeParse(); };
probeDispatch.on(("probeLoad."+this.label), probeParseBound);
};

Multiple Timers with setTimeInterval

I am facing a problem with setInterval being used in a loop.
I have a function subscribeFeed( ) which takes an array of urls as input.
It loops through the url array and subscribes each url to getFeedAutomatically() using a setInterval function.
so if three URL's are there in the array, then 3 setInterval's will be called.
The problem is
1)how to distinguish which setInterval is called for which URL.
2)it is causing Runtime exception in setInterval( i guess because of closure problem in javascript)
//constructor
function myfeed(){
this.feedArray = [];
}
myfeed.prototype.constructor= myfeed;
myfeed.prototype.subscribeFeed =function(feedUrl){
var i=0;
var url;
var count = 0;
var _this = this;
var feedInfo = {
url : [],
status : ""
};
var urlinfo = [];
feedUrl = (feedUrl instanceof Array) ? feedUrl : [feedUrl];
//notifyInterval = (notifyInterval instanceof Array) ? notifyInterval: [notifyInterval];
for (i = 0; i < feedUrl.length; i++) {
urlinfo[i] = {
url:'',
notifyInterval:5000,// Default Notify/Refresh interval for the feed
isenable:true, // true allows the feed to be fetched from the URL
timerID: null, //default ID is null
called : false,
position : 0,
getFeedAutomatically : function(url){
_this.getFeedUpdate(url);
},
};
urlinfo[i].url = feedUrl[i].URL;
//overide the default notify interval
if(feedUrl[i].NotifyInterval /*&& (feedUrl[i] !=undefined)*/){
urlinfo[i].notifyInterval = feedUrl[i].NotifyInterval;
}
// Trigger the Feed registered event with the info about URL and status
feedInfo.url[i] = feedUrl[i].URL;
//Set the interval to get the feed.
urlinfo[i].timerID = setInterval(function(){
urlinfo[i].getFeedAutomatically(urlinfo[i].url);
}, urlinfo[i].notifyInterval);
this.feedArray.push(urlinfo[i]);
}
}
// The getFeedUpate function will make an Ajax request and coninue
myfeed.prototype.getFeedUpdate = function( ){
}
I am posting the same on jsfiddle
http://jsfiddle.net/visibleinvisibly/S37Rj/
Thanking you in advance
After some prototyping i found a answer ,which has the answer,move the closure outside
function myclass(){
}
myclass.prototype.funone= function(){
var counter =0;
var timerID;
timerID = setInterval( function(){
alert(counter++);
},1000);
}
myclass.prototype.funtwo= function(){
var timerID2;
var counter2 =50;
timerID2 = setInterval( function(){
alert(counter2++);
},2000);
}
myclass.prototype.funthree = function( ){
var urlArray =["google.com","yahoo.com"];
var timeArray =[15000,6000];
var timerID ;
for(var i=0;i<2; i++){
var url = urlArray[i];
var timerinterval = timeArray[i];
timerID = this.register( url,timerinterval);
}
}
myclass.prototype.register = function(url,timerInterval){
var myUrl =url;
var myTimer = timerInterval;
var timerID = setInterval( function(){
alert(myUrl+"with"+ myTimer);
},myTimer);
}
var m = new myclass( );
m.funthree( );
http://jsfiddle.net/visibleinvisibly/Q4SBG/13/
The move the index binding from the setInterval and pass the url and time interval.
It works perfectly
You might want to have a look at this answer (under "The this variable" at the bottom) about what the this value means.
The error in your code may have something to do with using a counter in a loop and creating closures depending on the counter. The simplest way to create such closures is.
for(i=0;i<len;i++){
object.myCallback = (function(counter){
return function(){
doSomethingWith(counter);
}
}(i));
}
When creating closures on the fly like that you should be careful not dragging large or large amounts of variables into the closure scope. The link above and code below shows how to do this safely.
I've changed some of the code to make it simpler and not copy stuff that doesn't need to be copied, the setInterval is setTimeout so it only does it once but it's the same idea.
//constructor
function MyFeed(){
this.feedArray = [];
}
MyFeed.prototype.subscribeFeed =function(feedUrl){
var i=0,urlInfo=[];
feedUrl = (feedUrl instanceof Array) ? feedUrl : [feedUrl];
for (i = 0; i < feedUrl.length; i++) {
feedUrl[i].isEnable=true;
feedUrl[i].called=false;
feedUrl[i].position=0;//not sure what this is supposed to do
//Set the interval to get the feed.
feedUrl[i].timerID = setTimeout(this.closures//changed this to timeout
.getFeedUpdate(this)
,feedUrl[i].notifyInterval||100//changed default value
);
this.feedArray.push(feedUrl[i]);
}
};
// The getFeedUpate function will make an Ajax request and coninue
MyFeed.prototype.getFeedUpdate = function( index ){
console.log("in getFeedUpdate, this is now:",this);
console.log("my feed url object:",this.feedArray[index].url);
};
//limit closure scope, define closure creators here
MyFeed.prototype.closures={
//this.closures.getFeedUpdate(this)
// will return a closure that calls this.getFeedUpdate
// with correct parameters
getFeedUpdate:function(me){
var index = me.feedArray.length;
return function(){
me.getFeedUpdate(index);
};
}
};
//code to test adding single feed
var mf = new MyFeed();
mf.subscribeFeed({
url:"I am last",
notifyInterval:1000
});
//add another single feed
mf.subscribeFeed({
url:"first.com"
});
//and another
mf.subscribeFeed({
url:"second.com"
});
//and add some more feeds in an array of feeds
mf.subscribeFeed([
{
url:"third"
},
{
url:"fifth"
},
{
url:"no, I am last",
notifyInterval:1500
}
]);
Try FireFox with the FireBug plugin or Chrome and press F12 to see the console, when the log statements log something you can click on it to see the details of the logged item. Very helpful to log objects like this or simple values like index

How to detect creation of new global variables?

I want watch for the creation of new global variables in Javascript so that, anytime a global variable is created, an event is fired.
I've heard of the watch() function but that is only for watching for specific variable names. I want a catchall.
If you already know which names pollute your global namespace (see Intercepting global variable definition in javascript), you can use this trick to figure out when does it actually happen:
window.__defineSetter__('someGlobalVar', function() {
debugger;
});
Be sure to have your developer tools open when you run this.
Obviously works only if your browser supports __defineSetter__ but that's true for modern browsers. Also, don't forget to remove your debug code after you've finished.
Found here.
I don't know how to make this work "on demand" as soon as a var is created, but I can suggest a polling approach. In a browser window, all global become a member of the global "window" object. (Because technically, "window" is the "global object"). So you could do something like the following:
1) enumerate all the properties on a window
window.proplist = window.proplist || {};
for (propname in window) {
if (propname !== "proplist") {
window.proplist[propname] = true;
}
}
2) Set a timer to periodically "poll" window for new properties
setInterval(onTimer, 1000);
3) Wake up on the timer callback and look for new props
function onTimer() {
if (!window.proplist) {
return;
}
for (propname in window) {
if (!(window.proplist[propname])) {
window.proplist[propname] = true;
onGlobalVarCreated(propname);
}
}
}
Afaik, .watch() is only SpiderMonkey (Firefox).
I played around with a polling function, I finally came up with this:
var mywatch = (function() {
var last = {
count: 0,
elems: {}
};
return function _REP(cb) {
var curr = {
count: 0,
elems: {}
},
diff = {};
for(var prop in window) {
if( window.hasOwnProperty(prop) ) {
curr.elems[prop] = window[prop]; curr.count++;
}
}
if( curr.count > last.count ) {
for(var comp in curr.elems) {
if( !(comp in last.elems) ) {
diff[comp] = curr.elems[comp];
}
}
last.count = curr.count;
last.elems = curr.elems;
if(typeof cb === 'function')
cb.apply(null, [diff]);
}
setTimeout(function() {
_REP(cb);
}, 400);
};
}());
And then use it like:
mywatch(function(diff) {
console.log('NEW GLOBAL(s): ', diff);
});
Be aware that this only handles new globals. But you can easily extend this for the case last.count > curr.count. That would indicate that global variables were deleted.
You cant have an event fired when some script does var v = 10, but as selbie said, you can poll the window object... I meant to suggest the same, but he beat me to it. Here's my other example... you count how many window objects are there, and execute GlobalVarCreated() function:
var number_of_globals = 0; //last known globals count
var interval = window.setInterval(function(){
var new_globals_count = 0; //we count again
for(var i in window) new_globals_count++; //actual counting
if(number_of_globals == 0) number_of_globals = new_globals_count; //first time we initialize old value
else{
var number_of_new_globals = new_globals_count - number_of_globals; //new - old
if(number_of_new_globals > 0){ //if the number is higher then 0 then we have some vars
number_of_globals = new_globals_count;
for(var i = 0; i<number_of_new_globals; i++) GlobalVarCreated(); //if we have 2 new vars we call handler 2 times...
}
}
},300); //each 300ms check is run
//Other functions
function GlobalVarCreated(){}
function StopInterval(){window.clearInterval(interval);}
You can load up that code in Chrome or FF console only change: function GlobalVarCreated(){console.log("NEW VAR CREATED");} and test it:
var a = 10
b = 10
String NEW VAR CREATED is displayed 2 times.

Categories