Javascript class in closure - javascript

I'm writing a JavaScript closure that contains classes and looks a bit like this:
// myClosure.js
var myClosure = (function() {
class myClass {
constructor(val) {
this.myValue = val;
}
myFunc() {
console.log("myClass says: " + this.myValue);
}
}
this.myClass = myClass;
});
I'd like to 'import' this closure into another JavaScript file and instantiate a myClass. I tried this:
// myApp.js
$.getScript('myModule.js', function(myClosure) {
var myClassInstance = new myClosure.myClass(7);
}
but I get an error claiming that myClosure.myClass is not a constructor.
Can someone please point to a simple example, or a better way of doing this altogether?

Couple of things, myClosure returns nothing, so you will never be able to access it. In your example you could just return this.
Next, you have not executed the closure, you can do this by putting () at then end to make it into an IFFE..
Below is an example.
// myClosure.js
var myClosure = (function() {
class myClass {
constructor(val) {
this.myValue = val;
}
myFunc() {
console.log("myClass says: " + this.myValue);
}
}
this.myClass = myClass;
return this; //add this
}()); //and <- () that.
var myClassInstance = new myClosure.myClass(7);
myClassInstance.myFunc();

this doesn't work anything like that
myClosure is a function which doesn't create a closure
The first argument to the callback to getScript is a string containing the script. It isn't a value from the script.
To get values from it, you need to access a global.
Typically, if you were using jQuery.getScript, it would look something like this:
// myModule.js
class myClass {
// etc
}
// main.js
$.getScript('myModule.js', function () {
const myInstance = new myClass(7);
});
Modern code would tend towards using ES6 modules:
// myModule.js
class myClass {
// etc
}
export default myClass;
// main.js
import myClass from "./myModule.js";
const myInstance = new myClass(7);
// HTML
<script type="module" src="main.js"></script>
… although that sacrifices Internet Explorer support, in which cases you would want to look at using a bundler tool like Webpack.

you need to export this function first to include it in another javascript file.
if you are using node simply use module.exports = function name or for es6 you can use export default
and in another file simply import it

Related

How can I make my JavaScript library global?

I want to create a JavaScript library and have it readily available everywhere, exactly as Moment.js does.
For example, when I import Moment.js I can use it wherever I want by just typing:
moment.someMomentFunction()
Similarly, I'd want to call my function from everywhere by just doing:
mylib.function1()
mylib.function2()
etc..
Looking at the Moment.js source code I cannot see any reference to the window object (which is the one you should use if you want your object to be global)
EDIT: If you specifically want to create a library, export / import should help you:
import * as myModule from '/modules/my-module.js';
Export a function in the library with the export keyword:
export function bing(Parameters here) {
alert("bong");
}
As answered in Calling a javascript function in another js file
You can only call functions from files that were loaded before and are in the same scope. If the accepted answer from the refered post doesn't work, try jQuery
$.getScript(url, function() {
bläh();
});
Ordinary I use something like this to define a separate module using ES5.
LibName= window.LibName || {};
LibName = function () {
var yourVar1;
var yourVar2;
publicFunc1 = function() {
};
privateFunc2 = function() {
};
return {
"publicFuncName" : publicFunc1
}
}();
With the help of ES6, you may create with class and without window variable.
class MyLib {
constructor() {
this.foo = 1;
this.bar = 2;
}
myPublicMethod1() {
return this.foo;
}
myPublicMethod2(foo) {
return foo + this.bar;
}
}
const instance = new MyLib();
export { instance as MyLib };
In code, you going to use it like this
import { MyLib } from './MyLib';

How do you define a function of a class in another Javascript file using Node require?

Let's say I have a class A defined in its own JavaScript file, like this:
A.js
class A {
constructor() {
// blah blah blah
}
func() {
// a long function
}
}
If I have a function (e.g. func()) that I want to be contained in its own file (for organizational purposes), how would I accomplish this?
What I want is something like this:
A.js
class A {
constructor() {
this.func = {};
}
} exports.A = A;
ADefinition.js
var A = require('./A.js');
A.func = () => {
// a long function
}
This obviously doesn't work, but how would one accomplish this?
Classes are mostly just syntax sugar. In ES5, you define prototype functions by assigning to the prototype:
function A() {
}
A.prototype.func = function() {
}
Classes can work the same way:
var A = require('./A.js');
A.prototype.func = () => {
// a long function
}
Though, note that if you use an arrow function, you won't have access to the instance - you may well need a full-fledged function instead:
A.prototype.func = function() {
// a long function
};
Also, personally, I think I'd prefer to put func on the class next to the class definition for code clarity, rather than running a module that performs side effects (like your current code is attempting to do), for example:
const func = require('./func.js');
class A {
constructor() {
}
}
A.prototype.func = func;

Split a Javascript class (ES6) over multiple files?

I have a Javascript class (in ES6) that is getting quite long. To organize it better I'd like to split it over 2 or 3 different files. How can I do that?
Currently it looks like this in a single file:
class foo extends bar {
constructor(a, b) {} // Put in file 1
methodA(a, b) {} // Put in file 1
methodB(a, b) {} // Put in file 2
methodC(a, b) {} // Put in file 2
}
Thanks!
When you create a class
class Foo extends Bar {
constructor(a, b) {
}
}
you can later add methods to this class by assigning to its prototype:
// methodA(a, b) in class Foo
Foo.prototype.methodA = function(a, b) {
// do whatever...
}
You can also add static methods similarly by assigning directly to the class:
// static staticMethod(a, b) in class Foo
Foo.staticMethod = function(a, b) {
// do whatever...
}
You can put these functions in different files, as long as they run after the class has been declared.
However, the constructor must always be part of the class declaration (you cannot move that to another file). Also, you need to make sure that the files where the class methods are defined are run before they are used.
Here's my solution. It:
uses regular modern classes and .bind()ing, no prototype. (EDIT: Actually, see the comments for more on this, it may not be desirable.)
works with modules. (I'll show an alternative option if you don't use modules.)
supports easy conversion from existing code.
yields no concern for function order (if you do it right).
yields easy to read code.
is low maintenance.
unfortunately does not play well with static functions in the same class, you'll need to split those off.
First, place this in a globals file or as the first <script> tag etc.:
BindToClass(functionsObject, thisClass) {
for (let [ functionKey, functionValue ] of Object.entries(functionsObject)) {
thisClass[functionKey] = functionValue.bind(thisClass);
}
}
This loops through an object and assigns and binds each function, in that object, by its name, to the class. It .bind()'s it for the this context, so it's like it was in the class to begin with.
Then extract your function(s) from your class into a separate file like:
//Use this if you're using NodeJS/Webpack. If you're using regular modules,
//use `export` or `export default` instead of `module.exports`.
//If you're not using modules at all, you'll need to map this to some global
//variable or singleton class/object.
module.exports = {
myFunction: function() {
//...
},
myOtherFunction: function() {
//...
}
};
Finally, require the separate file and call BindToClass like this in the constructor() {} function of the class, before any other code that might rely upon these split off functions:
//If not using modules, use your global variable or singleton class/object instead.
let splitFunctions = require('./SplitFunctions');
class MySplitClass {
constructor() {
BindToClass(splitFunctions, this);
}
}
Then the rest of your code remains the same as it would if those functions were in the class to begin with:
let msc = new MySplitClass();
msc.myFunction();
msc.myOtherFunction();
Likewise, since nothing happens until the functions are actually called, as long as BindToClass() is called first, there's no need to worry about function order. Each function, inside and outside of the class file, can still access any property or function within the class, as usual.
I choose to have all privte variables/functions in an object called private, and pass it as the first argument to the external functions.
this way they have access to the local variables/functions.
note that they have implicit access to 'this' as well
file: person.js
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
// delegate getAge to PersonGetAge in an external file
// pass this,privates,args
this.getAge=function(...args) {
return PersonGetAge.apply(this,[privates].concat(args));
}
// delegate setAge to PersonSetAge in an external file
// pass this,privates,args
this.setAge=function(...args) {
return PersonSetAge.apply(this,[privates].concat(args));
}
}
file: person_age_functions.js
exports.PersonGetAge =function(privates)
{
// note: can use 'this' if requires
return privates.age;
}
exports.PersonSetAge =function(privates,age)
{
// note: can use 'this' if requires
privates.age=age;
}
file: main.js
const { Person } = require('./person.js');
let me = new Person();
me.setAge(17);
console.log(`I'm ${me.getAge()} years old`);
output:
I'm 17 years old
note that in order not to duplicate code on person.js, one can assign all functions in a loop.
e.g.
person.js option 2
const { PersonGetAge, PersonSetAge } = require('./person_age_functions.js');
exports.Person = function () {
// use privates to store all private variables and functions
let privates={ }
{
// assign all external functions
let funcMappings={
getAge:PersonGetAge,
setAge:PersonSetAge
};
for (const local of Object.keys(funcMappings))
{
this[local]=function(...args) {
return funcMappings[local].apply(this,[privates].concat(args));
}
}
}
}
You can add mixins to YourClass like this:
class YourClass {
ownProp = 'prop'
}
class Extension {
extendedMethod() {
return `extended ${this.ownProp}`
}
}
addMixins(YourClass, Extension /*, Extension2, Extension3 */)
console.log('Extended method:', (new YourClass()).extendedMethod())
function addMixins() {
var cls, mixin, arg
cls = arguments[0].prototype
for(arg = 1; arg < arguments.length; ++ arg) {
mixin = arguments[arg].prototype
Object.getOwnPropertyNames(mixin).forEach(prop => {
if (prop == 'constructor') return
if (Object.getOwnPropertyNames(cls).includes(prop))
throw(`Class ${cls.constructor.name} already has field ${prop}, can't mixin ${mixin.constructor.name}`)
cls[prop] = mixin[prop]
})
}
}
TypeScript Solution
foo-methods.ts
import { MyClass } from './class.js'
export function foo(this: MyClass) {
return 'foo'
}
bar-methods.ts
import { MyClass } from './class.js'
export function bar(this: MyClass) {
return 'bar'
}
class.ts
import * as barMethods from './bar-methods.js'
import * as fooMethods from './foo-methods.js'
const myClassMethods = { ...barMethods, ...fooMethods }
class _MyClass {
baz: string
constructor(baz: string) {
this.baz = baz
Object.assign(this, myClassMethods);
}
}
export type MyClass = InstanceType<typeof _MyClass> &
typeof myClassMethods;
export const MyClass = _MyClass as unknown as {
new (
...args: ConstructorParameters<typeof _MyClass>
): MyClass;
};
My solution is similar to the one by Erez (declare methods in files and then assign methods to this in the constructor), but
it uses class syntax instead of declaring constructor as a function
no option for truly private fields - but this was not a concern for this question anyway
it does not have the layer with the .apply() call - functions are inserted into the instance directly
one method per file: this is what works for me, but the solution can be modified
results in more concise class declaration
1. Assign methods in constructor
C.js
class C {
constructor() {
this.x = 1;
this.addToX = require('./addToX');
this.incX = require('./incX');
}
}
addToX.js
function addToX(val) {
this.x += val;
return this.x;
}
module.exports = addToX;
incX.js
function incX() {
return this.addToX(1);
}
module.exports = incX;
2. Same, but with instance fields syntax
Note that this syntax is a Stage 3 proposal as of now.
But it works in Node.js 14 - the platform I care about.
C.js
class C {
x = 1;
addToX = require('./addToX');
incX = require('./incX');
}
Test
const c = new C();
console.log('c.incX()', c.incX());
console.log('c.incX()', c.incX());

Closure compiler removing class namespaces

I have a bunch of JavaScript prototypes inheriting from each other. Each class is defined in its own file like this. An example file looks like this:
goog.provide("app.classes.ClassA");
(function() {
app.classes.ClassA = function() {
// constructor
}
app.classes.ClassA.prototype.example = function() {
// example method
}
})();
A second file may look like this:
goog.provide("app.classes.ClassB");
goog.require("app.classes.ClassA");
(function() {
app.classes.ClassB = function() {
// constructor
}
goog.inherits(app.classes.ClassB, app.classes.ClassA);
app.classes.ClassB.prototype.example = function() {
// example method
}
})();
After running the compiler, the code specified in the first file looks like this:
goog.provide("app.classes.ClassA");
(function() {
a.classes.ClassA = function() {
// constructor
}
a.classes.ClassA.prototype.example = function() {
// example method
}
})();
'app' in ClassA has been replaced by a single 'a' and goog.inherits cannot find app.classes.ClassA because it is undefined.
How can I prevent the Closure Compiler from renaming namespaces like this?
Thanks!
Two ways, first you can annotate your external methods/variables/classes with #expose:
(function() {
/**
* #expose
*/
a.classes.ClassA = function() {
// constructor
}
a.classes.ClassA.prototype.example = function() {
// example method
}
})();
Or, you can disable optimizations when running the closure compiler:
java -jar ccompiler.jar --compilation_level WHITESPACE_ONLY ..

What is the name of the javascript "namespace" pattern, that Typescript compiles to?

By default, this Typescript code
module Demo {
export class HelloWorld {
sayHello() {
return "Hello World";
}
}
}
compiles to the following Javascript
var Demo;
(function (Demo) {
var HelloWorld = (function () {
function HelloWorld() {
}
HelloWorld.prototype.sayHello = function () {
return "Hello World";
};
return HelloWorld;
})();
Demo.HelloWorld = HelloWorld;
})(Demo || (Demo = {}));
Is there a name for this Javascript namespace pattern and is it feasible to use it when writing pure Javascript (for example are there any better solutions)?
Very much so. The main reason is that it prevents name collision. e.g. consider the following javascript code:
function format(str){
// some dummy code
return str + " formatted";
}
function format(num){
// some dummy code
return num.toPrecision(10);
}
// beyond this point you cannot access format("str") version since it was replaced by format(number)
With typescript internal modules, you could just do:
module stringUtils {
export function format(str:string){
// some dummy code
return str + " formatted";
}
}
module numberUtils{
export function format(num:number){
// some dummy code
return num.toPrecision(10);
}
}
stringUtils.format('some string');
numberUtils.format(123);
Additionally it allows you to prevent global namespace pollution when you want private functions local to your code, e.g consider this case:
module Utils{
// Cache and compiled the regex
// This regext is not very useful outside this module
var trimRegex = RegExp("^\s+|\s+$");
// This function is useful so we export it
export function trim(str:string) {
return str.replace(trimRegex,'');
}
}
// You cannot use trimRegex now.
// Your namespace is clean
// you can use Utils.trim though :)
And as Steve Fenton mentions, this pattern is actively used in JavaScript as well (called self executing anonymous function).

Categories

Resources