Is it possible for a factory function imported from a module to return a closure that has access to the importing scope? Struggling to understand this in the world of closures and IIFEs. See below
module.js
function makeClosure() {
return function() {
//I want to access variables from any context where makeClosure is called here
//I always know variable names here
//eg:
console.log(someVar)
}
}
export default makeClosure
anotherModule's.js
import makeClosure from './module.js'
let someVar = 'hello!'
let closure = makeClosure()
closure() //print 'hello!'
I get a working closure if makeClosure is defined in the module where it is being used, but not if importing from another module. The application in this case is to put a set of functions that operate on the Google Maps API in a module so I can reuse them with different map instances in different parts of my app.
Related
I'm trying to use a class defined in a library but I only receive an error as a result.
[LibraryProject]/library/model/Update.gs
class Update {
constructor(obj = {}) {
if(typeof obj == "string"){
options = JSON.parse(obj);
}
Object.assign(this, obj);
}
text(){
return (this.message && this.message.text)?this.message.text:''
}
}
TASKS
✅ Create a new version of the project. (File > Manage versions...)
✅ Load this library in another project [Alias: CustomService] (Resources > Libraries...)
✅ Use functions of CustomService
❌ Use class of CustomService
If I try to use a Class
[NormalProject]/index.gs
function test (){
Logger.log(CustomService.libraryFunction())
var update = new CustomService.Update("");
Logger.log(update)
}
TypeError: CustomService.Update is not a constructor (línea 3, archivo "Code")
How can I instantiate an Object of this Class?
If I run...
Logger
As written in the official documentation,
Only the following properties in the script are available to library users:
enumerable global properties
function declarations,
variables created outside a function with var, and
properties explicitly set on the global object.
This would mean every property in the global this object are available to library users.
Before ES6, All declarations outside a function (and function declaration themselves) were properties of this global object. After ES6, There are two kinds of global records:
Object record- Same as ES5.
Function declarations
Function generators
Variable assignments
Declarative record - New
Everything else - let, const, class
Those in the declarative record are not accessible from the global "object", though they are globals themselves. Thus, the class declaration in the library is not accessible to library users. You could simply add a variable assignment to the class to add a property to the global object(outside any function):
var Update = class Update{/*your code here*/}
References:
Library official documentation
Global environment records
Related Answers:
ES6- What about introspection
Do let statements create properties on the global object
Based on your tests, it appears that you cannot directly import a class from a GAS library. I'd recommend creating a factory method to instantiate the class instead.
Something along these lines:
// Library GAS project
/**
* Foo class
*/
class Foo {
constructor(params) {...}
bar() {...}
}
/* globally accessible factory method */
function createFoo(fooParams) {
return new Foo(fooParams);
}
// Client GAS project
function test() {
var foo = FooService.createFoo(fooParams);
Logger.log(foo.bar());
}
So I have this file called lets say "fruits.js"
In this module, I have a global variable "name" with value "apples"
I also have a getter and setter for this variable to get the value of the variable name and to set a new to it.
now I imported this module into a file called "client1.js", and ran getter, it showed "apples".
now I ran setter with value "coconuts" and changed the variable.
now after client1.js finishes the execution, client2.js starts the execution with the same copied code.
and instead of "apples" it shows "coconuts".
So my question is, do multiple imports of the same module share the same lexical scope ?
code -
// fruits.js
let fruit = "apples";
const set = () => {
let flower = "lily";
const setfruit = (newfruit) => {
fruit = newfruit;
}
const getfruit = () => {
return fruit;
}
const setflower = (newflower) => {
flower = newflower;
}
const getflower = () => {
return flower;
}
return {
setfruit,
getfruit,
setflower,
getflower
}
}
module.exports = set();
// client1.js
const fruitsnflowers = require("./closure.js")
const execute = () => {
console.log(fruitsnflowers.getfruit())
console.log(fruitsnflowers.getflower())
fruitsnflowers.setfruit("coconut")
fruitsnflowers.setflower("hibiscus")
}
module.exports = execute;
// client2.js
const fruitsnflowers = require("./closure.js")
const execute = () => {
console.log(fruitsnflowers.getfruit())
console.log(fruitsnflowers.getflower())
fruitsnflowers.setfruit("papaya")
fruitsnflowers.setflower("lotus")
}
module.exports = execute;
// driver.js
const client1 = require("./client1.js")
const client2 = require("./client2.js")
client1()
client2()
// output
~ node driver.js
apples
lily
coconut
hibiscus
The module's code is loaded and initialized only once when it is first imported. After that, the exact same module handle is returned from subsequent attempts to import it.
So, in your case, when you do module.exports = set(), you are running that functions set() which returns an object that has references to local functions that can see the local scope. Anyone who imports the function will get the exact same exported object so therefore, they will all point to the same local scope (within this module).
If you call setFruit("banana") from within moduleA and then call getFruit() from within moduleB, you will get back "banana". The fruits module only has one set of state and only one internal fruits variable that all calls to it's exported methods have access to.
So my question is, do multiple imports of the same module share the same lexical scope ?
Your use of the term "lexical scope" here is a bit confusing. Two modules that import this other module each have their own lexical scope, but the code inside the imported module lives in its own scope that does not change no matter who imports it.
So, if moduleA and moduleB each import your fruits module, there will be three scopes as each module has its own lexical scope at the top level. When you call a fruit function, that function operates internally (inside the execution of the function) within the fruit lexical scope. When you get the return value back from within moduleA or moduleB, you are then operating within those module's scope. There is no mingling of the scopes. Each module has its own top level scope.
I'm getting rather confused as to if something is possible or not.
I create a module that contains the following:
export function logText(){
console.log('some text');
}
export class Example {
constructor(){
logText();
}
}
The intent is for the user to call new Example to start off the module logic.
import { logText, Example } from 'example';
// Do some magic here to modify the functionality of logText
new Example();
Is it possible for the end user to modify logText?
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
I frequently see repos with lots of functions exported that are useless without the users having to remake almost all the functionality manually, making it rather pointless to do. One good example is this repo whre theuy even call the exported functions their 'API'. In that example these are rather pointless exports and at worse would just cause issues if someone tried to use them in conjunction with the main function. But if you could modify them and have them still run then it would make sense to me.
Given this:
import { logText, Example } from 'example';
Is it possible for the end user to modify logText?
Since you aren't being very specific about what you mean by "modify logText", I'll go through several options:
Can you reassign some other function to the variable logText?
No. You cannot do that. When you use import, it creates a variable that is const and cannot be assigned to. Even if it wasn't const, it's just a local symbol that wouldn't affect the other module's use of its logText() anyway. The import mechanism is designed this way on purpose. A loader of your module is not supposed to be able to replace internal implementation pieces of the module that weren't specifically designed to be replaced.
Can you modify the code inside of the logText function from outside of the module that contains it?
No, you cannot. The code within modules lives inside it's own function scope which gives it privacy. You cannot modify code within that module from outside the module.
Can you replace the logText() function inside the module such that the implementation of Example inside that class will use your logText() function?
No, you cannot do that from outside the module. You would have to actually modify the module's code itself or someone would have to design the Example interface to have a replaceable or modifiable logText() function that the Example object used.
For example, logText() could be made a method on Example and then you could override it with your own implementation which would cause Example's implementation to use your override.
Code in the module that you do not modify:
export class Example {
constructor(){
this.logText();
}
logText() {
console.log('some text');
}
}
Code doing the import:
import { Example } from 'example';
class MyExample extends Example {
constructor() {
super();
}
logText() {
console.log("my own text");
}
}
let o = new MyExample();
Can you create your own version of logText and use it locally?
Sure, you can do that.
function myLogText() {
do your own thing
}
And, you could even NOT import logText so that you could use the symbol name logText() locally if you wanted. But, that won't affect what Example does at all.
Are there ways to design the example module so that that logText() can be easily replaced.
Yes, there are lots of ways to do that. I showed one above that makes logText a method that can be overriden. It could also be passed as an optional argument to the Example constructor.
There could even be an exported object that allowed the caller to replace properties on that object. For example:
export const api = {
logText: function logText(){
console.log('some text');
}
};
export class Example {
constructor(){
api.logText();
}
}
Then, use it like this:
import { api, Example } from 'example';
api.logText = function() {
console.log('my Text');
};
I would generally not recommend this because it sets you up for usage conflicts between multiple users of the same module where each one tries to modify it globally in ways that conflict with each other. The subclassing model (mentioned above) lets each use of the module customize in its own way without conflicting with other usages of the module.
Is it possible for the end user to modify logText?
No, that's not possible, import bindings are immutable, and function objects are basically immutable wrt the code they contain.
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
Why not make the log function an optional argument in the constructor? Usually when something is variable it becomes a parameter.
export class Example {
constructor(log=logText){
log();
}
}
Export Objects {} vs Export function
I'm developing an exercise application, I came across to the question When do I need to exports an object {} instead of a function class?
Scenario example:
I'm building a simple authentication module using the object style.
// file auth.js
module.exports = {
login: function() {
// code login
},
logout: function() {
// code logout
},
register: function() {
// code register
}
}
Here I'm using the anonymous function style
module.exports = function() {
return {
login: function() {
// code login
},
logout: function() {
// code logout
},
register: function() {
// code register
}
}
}
Requiring
When I want to require this module I just do:
var auth = require('auth');
auth.login(); // trigger login function via object
auth().login() // trigger login function via function
It will work with both the approaches, but I'm confused to choose which fit better and why.
Questions
How do you understand in your design of a module, when is appropriate to exports, an object, anonymous function, named function to Instantiate?
Which are the difference and how the require method behave, when requiring these functions or Objects ?
How do you understand in your design of a module, when is appropriate to exports, an object, anonymous function, named function to Instantiate?
true minimalists aim to export a single function if that will suffice. This is based on the scope of your module and assumes a small and focused purpose.
Export an object of related functions when you need a set of functions to have a meaningful set of functionality. For example, a tempConversion module might have both cToF and fToC functions.
If you are doing OO, exporting the constructor function is fine.
a closure function that returns the real payload (which can be an object or a function) is useful for configuration options. For example var tip = require('computeTip')(18); could store 18 in closure and return a function that would calculate 18% tip when called with a number.
Here's a rule of thumb: if you export only one named function a la require('average').average(listOfNumbers), it's redundant, so just export the average function directly to make it more concise. If you have more than one function you want to expose, use an object whose properties are the functions.
Which are the difference and how the require method behave, when requiring these functions or Objects ?
There are no differences. require has a single behavior that accommodates each of these techniques.
In both your examples you actually return an object and not a function class. In the second case you get the object by executing the function. In order to use the function as a class you should return a function which acts as the constructor, and then instantiate with the new operator.
That being said, I would prefer an object over a class if I consider my module as a singleton, something that would be common to every file that would include it. An example would be a module that implements a common access layer to my database.
A class makes more sense if you intent to instantiate it multiple times. An example would be a specific model class for your database schema, like a User class.
Is there a way to create a private class in a separate file without polluting the global namespace? Currently I just create a sub-namespace and put all of the private classes that other public classes need to function, but is there a way to just make a class altogether private, yet in another file?
You are probably looking for the "Module Pattern" in JavaScript. The Module Pattern name can refer to a lot of different patterns but the basic concept to to declare functions and attributes that are only available to a function which has already been called. Like this:
myModule = function () {
var me = {},
iAmPrivate = 1;
function privateFunc() {
// stuff can access iAmPrivate and iamPublic
}
me.iamPublic = 1;
me.publicFunc = function () {
// stuff can access iAmPrivate and iamPublic
};
return me;
}());
myModule.publicFunc(); // this can be called
myModule.iamPublic; // this can be accessed
myModule.iAmPrivate // xx can't do this
myModule.privateFunc() // or this
Because of how closures and scope works, Items declared local to that function call are available to all functions declared in that function call but nothing outside of the function call. The function has been called so they can never be accessed.
You can read a lot more about it here : http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html, and of course with a Google search now that you know what it is called.