Function Scope Rules (Google Apps Script Project) - javascript

If I have several files in a single Apps Script Project that have a function with the same name, how will the scope be determined?
By file?
By dynamic scoping?
By static scoping?
Time of function creation (script file created)?
For example if I have Stuff.gs:
function start() {
var number = getNumber();
}
function getNumber() {
return 5;
}
and More.gs:
function getNumber() {
return 10;
}
And I call start(), how does Google's platform determine which function to call?
I did a test like this, and I didn't get the expected output. The output is 10.0. It seems to me that neither file scope rules are applied, nor static scoping. I created a third file to test further:
Test.gs:
function getNumber() {
return 15;
}
and now the output is 15.0. I further tested and changed 10 to 20 in More.gs to see if the save timestamp determined the scope, but the output was still 15.0.
So to me it seems that the .gs file creation date determines the scope - the most recent timestamp on the file that contains the function name is used. Am I correct in my assumption or is this just a coincidence and it's determined in some other way?
Also, is this specific to Google's Apps Script, or Javascript in general?

Looks like those declarations are all just in global scope. Subsequent definitions will overwrite the previous ones, so if you are first including Stuff.gs, then More.gs, then Test.gs and are calling your function thereafter it would make sense.
The scoping in JS is static (assuming strict mode without with and local eval), but the global scope may be modified dynamically based on the loaded modules (and their order). This is the behaviour in the most JavaScript environments, some also have an additional file (module) scope.

Related

Using Java Script prototype Function in a google sheets external Library

I added a prototype function to String in google sheet project, and created a new version to be referenced in another project.
Then I referenced this new library version in another project (Resources-> Libraries in the script editor).
Now I want to use this function, but execution fails as it doesn't recognized this func.
Function definition in project1:
if (!String.prototype.newStringFunc) {
String.prototype.newStringFunc = function() {
Logger.log(this.toString());
};
}
Usage in Project2:
function test()
{
var s = "Hello";
s.newStringFunc();
}
Result:
TypeError: s.newStringFunc is not a function
If the function is defined in Project2 - all good.
Other "regular" functions within Project1, are recognized and executed properly.
Is it not possible to add a prototype function to an external library?
Prototype libraries used to work on old Rhino javascript interpreter.
So, if you turn your Project2 back to old Rhino javascript interpreter for project1, it doenst matter.
It will work again.
Just Go to Menu >> Execute >> Disable Chrome V8.
I don't really know for how long the old interpreter will be available, until then, lets use it.

Having two instances of Context Factory under the same JVM

I'm using Rhino to compile and execute javascript functions. In order to constrain the execution time of scripts, I created a custom ContextFactory where am able to set the max cpu time allowed and the number of instructions before calling observeInstructionCount, similarly to the example described in ContextFactory.
The following code is a sample of the method I use to compile and execute the functions.
CustomFactory factory = new CustomFactory().setMaxCpuTime(300L);
if (!ContextFactory.hasExplicitGlobal()) {
ContextFactory.initGlobal(factory );
}
Context context = factory.enterContext();
Scriptable Scope = context.initStandardObjects();
Function function = context.compileFunction(scope, script, "<func>", 0, null);
(...)
function.call(context, scope, scope, args);
Every thing works as excepted, the scripts that exceed the maximum allowed cpu time throw an Error. My problem is that I want to have two instances of my custom factory under the same JVM, one that will allow the scripts to run for a little longer than the other. But because of the call ContextFactory.initGlobal(factory) every Context will inherit the same ContextFactory. Even if I create a new custom factory with a different allowed cpu time, when I call factory.enterContext() the context will inherit the factory that was passed to initGlobal.
Is there any way to have two instances of ContextFactory (with different properties) under the same JVM?

Manually Invoke IIFE

I've got a library (Hubspot Odometer) which I am using in a web application I am developing and it works fine to create and run Odometer style widgets on a page.
The trouble is that they are part of a dashboard interface which has panes that are loaded via AJAX. The initial view is not loaded via AJAX so the JavaScript executes fine and the odometers render correctly.
When I load a new pane with odometers however they are not rendered correctly nor do they act as they should. The reason for this is that the odometer library runs as one big IIFE.
What I am wondering is can I re-invoke the IIFE manually after I load content via AJAX so that the odometers are rendered and bound to correctly?
I am also using jQuery if that offers me any additional options.
Try this:
var funcName = (function funcName() {
// rest of the code
return funcName;
}());
Also see this jsbin.
The whole idea of the IIFE is that its an anonymous function that is immediately executed. So by definition, no there is no way to re-execute it.
With that said however, you can store the function expression to a global variable, and execute it. For example
window.my_iife = (function() { /* stuff */ });
window.my_iife();
Notice the slight difference in syntax compared to the traditional IIFE: ((function() {})());
By storing the function in window, you are able to access it later from either the developer console or anywhere else in your code. If you simply store it in a var, or declare it as function my_iife() { /* ... */ } somewhere you risk that var or function declaration itself being wrapped in an IIFE and thus being inaccessible. As an example, that scenario could occur if the file you declared your var/function in is part of a Sprockets manifest (such as application.js in Rails).
var funcName = null;
(funcName = function funcName() {
// rest of the code
return funcName;
}());
funcName();
This is what worked for me

How to parse and load javascript object?

I have one js files . I load it using other javascrupt file using eval() function. I have seen eval is slow and with some other limtation. Since i need to store my JS file object in cache and use it anytime i need after apllication starts. I dont want to do eval() everytime.
Is there anyway to do it in simple way.
var evalObj;
if(evalObj) {
console.log('eval object already evaluated');
_myfunctionInJSFile_(layouts.FormatDate(startTime), threadName, level, categoryName, message);
}
else {
evalObj = eval(fs.readFileSync('./myJSFile', 'utf8'));
console.log('re evaluating object ..' );
_myfunctionInJSFile_(layouts.FormatDate(startTime), threadName, level,message);
}
myJSFile
var _sigmaAlarmHandler_ =function(args)
{
var args = Array.prototype.slice.call(arguments);
args.unshift();
console.log('Alarm : ', args);
}
Either the conditional eval is not working.
In node.js you can simple require your js-file:
var obj = require('./myJSFile');
obj.foo();
./myJSFile.js:
exports.foo = function() {
console.log('foo');
}
This file becomes a module with exported functions, that you need.
It loads once, then every require reuse already loaded module.
If it is not commonjs-compliant (i.e. using module.exports will not work), then you can run it in its own vm:
var vm = require('vm');
vm.runInNewContext(jscode,{/*globalvars*/});
where the second parameter is an object with global vars made available in the context in which the jscode is run. So if the second param is, say, {a:1,b:"foo"} then your jscode will run with the global variable a set to 1 and the global variable b set to "foo".
The jscode itself is a string that you load from a file or elsewhere.
Think of vm.runInNewContext() as "practice safe eval". Well, relatively safe, you can still do some dangerous stuff if you pass in particular vars, like process or file etc.
I used this for the declarative part of cansecurity http://github.com/deitch/cansecurity for nodejs
You can view the sample in the file lib/declarative.js
Here is the API for vm http://nodejs.org/api/vm.html
There are options to run in the same context, etc. But that is very risky.
When you actually run the code, using your example above:
_myfunctionInJSFile_(layouts.FormatDate(startTime), threadName, level,message);
you are looking to pass in 4 params: startTime, threadName, level, message and execute the function. The issue is that you cannot run the function on the current context. You need the function to be defined and run in the file. So you should have something like:
vm.runInNewContext(jscode,{startTime:layouts.FormatDate(startTime),threadName:threadName,level:level,message:message});
And then the jscode should look like
function _myfunctionInJSFile(startTime,threadName,level,message) {
// do whatever you need to do
}
// EXECUTE IT - the above vars are set by the global context provide in vm.runInNewContext
_myfunctionInJSFile(startTime,threadName,level,message);
If you prefer to define the function and have it loaded and run in this context, then just use the commonjs format.
I think i have found the answer for this.
Since my application is running in node js which uses v8 engine platform. When the application starts v8 engine caches all the code/configuration and can be used anytime.
Similarly in my code i will pre-load the JS code using eval and i will do it only once. So on next call i will return only the loaded JS code. Here i need to modify the code to load once.
But main point we have look is that in future if any body has similar requirement they can cache their JS codes using eval (thanks to v8 engine) and use it till your application is running.

Tell the Closure Compiler not to rename any function

I want to compile part of my JS code which is based on Mootools library.
I want all the variables be renamed but none of the function, the called and defined ones. Because most of the called one are from mootools and those defined are called from outside:
code to be compiled:
// textnum is safe to be renamed, all variables are
textnum = 0;
// loadText can't be ranmed because is called from outside
function loadText()
{
textnum++;
document.body.setStyle("font", "12px");
// here setSyle can't be renamed
}
Is there a way to tell it to rename only vars?
I found out this is an open source project, Is there a way to manipulate it somehow that it doesn't touch function at all!?
Put the code to be compiled in a namespace or anonymous function wrapper and use simple optimizations. This renames all internal vars and functions but not global ones such as setStyle.
Functions which should not be renamed are defined in the global scope. This is not as much of a pain compared to defining externs and exports.

Categories