I have the next piece of code:
function Server() {
this.isStarted = false;
// var isStarted = false;
function status() {
return isStarted;
}
console.log(status());
}
var a = new Server()
When I run it I get
ReferenceError: isStarted is not defined
at status (/a/fr-05/vol/home/stud/yotamoo/workspace/ex4/text.js:7:10)
at new Server (/a/fr-05/vol/home/stud/yotamoo/workspace/ex4/text.js:10:14)
at Object.<anonymous> (/a/fr-05/vol/home/stud/yotamoo/workspace/ex4/text.js:
However if I change this.isStarted = false; to var isStarted = false; everything works fine.
Does anyone care to explain why?
Thanks
This referes to the owner of something. See this article about this. Where as var declares a local variable.
In your case, you want to refer to know if a server is started, so you need to add 'this' to your status function.
function status() {
return this.isStarted;
}
Long story short. Since isStarted, when defined as this.isStarted = true, is a property of the current object (JavaScript this keyword refers to an object where the function was called), in status function you will have to access it as this.isStarted.
Declaring it as variable (var) is different. Technically, isStatus will become a property of hidden lexical scope object. You have to access it as just isStatus in the whole Server function body and and all of the child functions.
Related
I bought a 3rd party google app script to use. However, it can only be called with onEdit method and their codes are private that i cannot make change. Also, what i need is based on time-trigger instead of onEdit-trigger. Thus, I tried to build my own event to trigger the function:
// This function work well and can call the 3rd Party App Script
// It is triggered by onEdit googlesheet, which works well
function funcOnEdit(e) {
3rdPartyApp.funcOnEdit(e));
}
// Below is the jsontostring result of the event e
// {authMode:"FULL",oldValue:"false",range:{columnEnd:6,columnStart:6,rowEnd:3,rowStart:3},source:{},triggerUid:"xxx",user:{email:"xxxx#gmail.com",nickname:"xxxx"},value:"TRUE"}
So I build a similar event object which triggered by time to make it happened.
function funcOnTimeTrigger(e) {
var e1 = {authMode:"FULL",oldValue:"false",range:{columnEnd:6,columnStart:6,rowEnd:3,rowStart:3},source:{},triggerUid:"xxx",user:{email:"xxxx#gmail.com",nickname:"xxxx"},value:"TRUE"};
e1.triggerUid = e.triggerUid;
3rdPartyApp.funcOnEdit(e1));
}
Unfortunately, I cannot find any document and reference code to build an "onEdit" event. Thats why, I tried find the object/class myself.
function getObjType(obj) {
var type = typeof(obj);
if (type === "object") {
try {
// Try a dummy method, catch the error
type = obj.getObjTypeXYZZY();
} catch (error) {
// Should be a TypeError - parse the object type from error message
// type = error.message.split(" object ")[1].replace('.','');
type = error.message;
}
}
return type;
}
// Below function is triggered by onEdit
function funcOnEdit_checker(e) {
getObjType(e);
}
// Unfortunately, it cannot show the object name or classname
I have no idea what to do next, may i know if it is possible to build an event class/object ourselves in Google Script App? Can anyone give some hints on how to do so? or it is not possible?
I want to create the event-obj "developers.google.com/apps-script/guides/triggers/events" manually and pass the event "e" to 3rdPartyApp.funcOnEdit function. Is it possible to do so?
Reference:
https://developers.google.com/apps-script/guides/triggers/installable
Thanks #Cooper idea, who share same thought as me.
And finally I found the result # Explain purpose of `e` event parameter in onEdit
Below is my answer (not yet optimized but work):
function funcOnTimeTrigger(e) {
var e2 = {}
e2["authMode"] = ScriptApp.AuthMode.FULL
e2['user'] = "me";
e2['range'] = SpreadsheetApp.getActive().getSheetByName("XXXXXX").getRange(5,3).activate();
e2.range.columnStart = 5;
e2.range.columnEnd = 5;
e2.range.rowStart = 3;
e2.range.rowEnd = 3;
e2['source'] = SpreadsheetApp.getActive();
e2['oldValue'] = "old";
e2['value'] = "new";
// Run The Program
3rdPartyApp.funcOnEdit(e2);
}
I have a JavaScript file which is loaded by require.
// loaded by require()
var a = this; // "this" is an empty object
this.anObject = {name:"An object"};
var aFunction = function() {
var innerThis = this; // "this" is node global object
};
aFunction();
(function(anyParameter){
console.log(anyParameter.anObject);
})(
this // "this" is same having anObject. Not "global"
);
My question is: this in var a = this; is an empty object whereas this statements in functions are shadows of node.js global object. I know this keyword is different in functions but I could not understand why first this is not equal to global and this in functions equals to global.
How does node.js inject global to this in function scopes, and why it does not inject it to the module scope?
Here's a few fundamental facts you must understand to clarify the situation:
In the top-level code in a Node module, this is equivalent to module.exports. That's the empty object you see.
When you use this inside of a function, the value of this is determined anew before each and every execution of the function, and its value is determined by how the function is executed. This means that two invocations of the exact same function object could have different this values if the invocation mechanisms are different (e.g. aFunction() vs. aFunction.call(newThis) vs. emitter.addEventListener("someEvent", aFunction);, etc.) In your case, aFunction() in non-strict mode runs the function with this set to the global object.
When JavaScript files are required as Node modules, the Node engine runs the module code inside of a wrapper function. That module-wrapping function is invoked with a this set to module.exports. (Recall, above, a function may be run with an abitrary this value.)
Thus, you get different this values because each this resides inside a different function: the first is inside of the Node-created module-wrapper function and the second is inside of aFunction.
To understand this, you need to understand that Node.js actually wraps your module code in to a function, like this
(function (exports, require, module, __filename, __dirname) {
var test = function(){
console.log('From test: ' + this);
};
console.log(this);
test();
});
Detailed explanation can be found in this answer.
Now, this wrapped function is actually invoked like this
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
So, this, at the module level, is actually the exports object.
You can confirm that like this
console.log(this, this === module.exports);
// {} true
Summary:
In Javascript the value of this is determined when a function is called. Not when a function is created. In nodeJS in the outermost scope of a module the value of this is the current module.exports object. When a function is called as a property of an object the value of this changes to the object it was called. You can remember this simply by the left-of-the-dot rule:
When a function is called you can determine the value of this by looking at the place of the function invocation. The object left of the dot is the value of this. If there is no object left of the dot the value of this is the module.exports object (window in browsers).
caveats:
This rule does not apply for es2015 arrow function which don't have their own binding of this.
The functions call, apply, and bind can bend the rules regarding the this value.
Example (NodeJS):
console.log(this); // {} , this === module.exports which is an empty object for now
module.exports.foo = 5;
console.log(this); // { foo:5 }
let obj = {
func1: function () { console.log(this); },
func2: () => { console.log(this); }
}
obj.func1(); // obj is left of the dot, so this is obj
obj.func2(); // arrow function don't have their own this
// binding, so this is module.exports, which is{ foo:5 }
Output:
It's because the default global object in a Node.js module is the exports object, and you are calling test() which doesn't specify this. In traditional JS, this points to the global object, with use strict, this will be null.
this can point to anything, it just depends on how you call it.
test(): Uses the global object (exports) as this, unless in strict mode, where this will be null;
test.call({}) or test.apply({}): You are specifying what to use as this (the first parameter)
var obj = {testRef: test}; obj.testRef(): this is set to the left of the ., that is, obj
Countering thefourtheye's answer
It is true that this in the top level of the module is exports, but that doesn't necessarily mean that this inside test() will also point to same thing as where it was called from.
Attempting to prove that this and the global object both point to exports
myGLobal = 5;
this.myGlobal; // 5
I am trying to make some modifications to a Node.js app that I forked, what I am trying to do is call a function within another one.
I have attempted a crack at this by simply calling the method as follows but I'm not really that familiar with Node.js so I'm not sure I'm doing it right.
'use strict';
/*
* initalize the class
*/
function MessageBot(bot, user, cache) {
this.bot = bot;
this.user = user;
this.cache = cache;
}
/*
* perform commands
*/
MessageBot.prototype.librarySearch = function(searchText) {
var self = this;
// call SOS function - this is the bit that doesn't work
MessageBot.prototype.callSos(somenumbervar);
}
MessageBot.prototype.callSos = function(number) {
// do something else here
var self = this;
var commandList = self.cache.get('commandList');
}
Remember that this ostensibly inherits the prototype (directly or indirectly). Therefore, you can just do this.callSos(somenumbervar).
If you want to reach the method through the prototype, you have to tell it what this is. With your current code, this in callSos() will be MessageBot.prototype -- certainly not what you want. So you can also do MessageBot.prototype.callSos.call(this, somenumbervar).
Which approach to take depends on how dynamic you want your objects to be. For example, if you want consumers of MessageBot to be able to "override" callSos() by installing their own method, then you should take the first approach (this.callSos()) as it will look up callSos in the object's inheritance chain. This process will only reach the method you've installed on the prototype if the method hasn't been overridden. IMO this is the approach you should take unless you have a very good reason not to.
See this example, which demonstrates how the two approaches differ regarding overriding, while also showing that both work with regards to passing the correct this value (since both methods can retrieve the expected value from this.data):
function ClassA(data) {
this.data = data;
}
ClassA.prototype.foo = function () {
console.log("this.bar() returned: " + this.bar());
console.log("ClassA.prototype.bar.call(this) returned: " + ClassA.prototype.bar.call(this));
};
ClassA.prototype.bar = function () {
return 'in the ClassA prototype, our data is ' + this.data;
};
console.log('--obj1-- (no override)');
var obj1 = new ClassA(3.14);
obj1.foo();
console.log('--obj2-- (override)');
var obj2 = new ClassA(42);
obj2.bar = function () {
return 'in an overriding method, our data is ' + this.data;
};
obj2.foo();
My application is a simple mysql client used from command line - it connects to database and makes few queries to get information from database. Mysql functionality is encapsulated in a class and problem is since calls to mysql server is async (understandably) - the code flow reaches end of application.
And I am unable to refer to 'this'(Mysql) inside a method of Mysql class.
How do I get around this problem ?
Below is my code.
//CLASS
function Mysql(config) {
//...
}
//METHOD
Mysql.prototype.getDbInfo = function (cbk) {
this.showTables(function(e,r) {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(this.configVar);
});
}
module.exports = Mysql;
//CLASS OBJECT
var test = new Mysql(config);
//METHOD INVOKE
test.getDbInfo(function (err,results) {
//...
});
Every time that you jump into a callback function you are loosing the scope of the this object. There are different ways to work around it.
Assign this to another variable
The first solution is to assign the this object to another variable (e.g.: that, self). When you assign one variable to another and the first variable is an object then you keep the reference to the original object and you can use it within the callback. Something like that:
Mysql.prototype.getDbInfo = function (cbk) {
var self = this;
self.showTables(function(e,r) {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(self.configVar);
});
}
Bind the this object to the function
You can bind the this object to the function and like that you set the this keyword set to the provided value (in your case the scope outside of showTables function). You can read the documentation of this and you will be able to understand more:
Mysql.prototype.getDbInfo = function (cbk) {
this.showTables(function(e,r) {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(self.configVar);
}.bind(this));
}
Use es6 arrow functions
It is more or less the same solution like the first one. If you use a transpiler you will find out that it is translated like the first solution:
Mysql.prototype.getDbInfo = function (cbk) {
self.showTables((e,r) => {
// >>>>>>>>>> PROBLEM HERE using 'this' <<<<<<<<<<<
console.log(this.configVar);
});
}
i have a question regarding defining objects in modules.
lets say i have a module:
/*---obj----*/
function A (param){
this.parm=param;
function func(){
//do somthing
}
}
exports.func=func;
/*---file.js----*/
obj=require('obj');
function fileFunc(A){
A.func();//with out this line it works fine
A.param=2;
}
}
for some reason it does not recognize the function in the object A. it recognizes the object A and its different vars, but when it comes to executing the function it gives the msg:
TypeError: Object # has no method 'func'
i tried also to export the function in A by:
exports.A.func=A.func
or
exports.func=func
neither works..
does someone have a clue?
thanx
matti
The function you defined inside of A is local only to that function. What you want is
function A(param) {
this.param = param;
}
A.func = function() {
// do something
};
But if you are treating A as a constructor then you'll want to put that function in A's prototype
A.prototype.func = function() {
// do something
};