I want to write a utility class that will hold several methods that will be used throughout my code.
say Util.sayHi("Maxim") // Hi Maxim but I want to try avoid pulling the global scope, and the possibility that someone would override my method.
I see jQuery has a solution to this problem https://learn.jquery.com/using-jquery-core/avoid-conflicts-other-libraries/ how do they do that?
I'm working with React, and ES6 / ES7 transformers so new JS features are acceptable.
Well, since you are using ES6, the solution is trivial: use modules. They're designed to do exactly this.
// util.js
export function sayHi(name) {
console.log("Hi "+name);
}
// somewhereelse.js
import * as Util from "util.js";
Util.sayHi("Maxim");
You're not polluting global scope at all, and the variables you are exporting are not writable from outside the module.
Related
Is is possible to have one class and distribute it's methods over multiple files?
My class is now in one file and it's getting to be a long file, that is becoming hard to navigate developing.
I thought of extending a class, but that is building on top of a class. And multiple extended classes don't communicate amongst each other, right?
So the main file would have the constructor and the other files would have one or just a few methods of its class.
You can assign to properties of the prototype, just like in the old days when functions were used instead of classes - import the functions from the other files, and after the class is defined, assign the properties:
// main.js
import method1 from './method1';
import method2 from './method2';
// or use require() syntax, depending on how your environment is set up
class Foo {
constructor(name) {
this.name = name;
// ...
}
}
Foo.prototype.method1 = method1;
Foo.prototype.method2 = method2;
// method1.js
export default function() {
console.log('method1 running for ' + this.name);
};
Both method1 and method2 will still be able to reference this to refer to the instance, just like they were defined as a method directly inside Foo.
You can use import syntax with ES6 modules (eg <script type="module") or in a module bundler system like Webpack. In node, import using require(path) and export by assigning to module.exports or properties of module.exports.
If a single class is getting so big that you're worried the file containing it is getting too big, I'd say the best thing you can do is refactor the class into smaller classes that are more focussed on solving a single problem.
If you can't do that, then you can have the main class's file create the class:
class Example {
}
...and then have multiple files that add methods to its prototype:
Example.prototype.doThis = function doThis() {
// ...
};
Example.prototype.doThat = function doThat() {
// ...
};
You lose the ability to use super in those methods, but other than that it works.
I thought of extending a class, but that is building on top of a class. And multiple extended classes don't communicate amongst each other, right?
I'm not sure what you mean about not being able to communicate between superclasses and subclasses. The instance data is common to all, and they can use each other's methods.
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();
}
}
I've recently written a huge library in ECMAScript2015 which I would like to write a type definition for, so that it can be used in TypeScript projects.
Let's call the lib 'myShop' for now, with the basic structure being:
myShop.init(params): // function
myShop.cart.goto(); // function in namespace 'cart'
myShop.info; // variable that contains trivial information
My question is what the best practices would be, for typing the library. For just the sake of getting autocomplete in IntelliJ, there are many ways that seem to work.
My approach so far looks like this:
declare module myShop {
function init(param:List<string>): Promise;
var cart: ICart;
var info: Object;
}
interface ICart {
goto(): void;
}
Any tips for improvement? Is an interface for 'cart' correct? Should it rather be a module?
Thank you in advance!
I have a class that has roughly this structure:
function MyClass() {
// constructur stuff
}
MyClass.prototype.myFunc = function () {
// example function
};
MyClass.myStaticFunc = function () {
// example static function
};
I spent some time now setting up the closure compiler annotations and finally got rid of all warnings. And what do you know, it reduces the size by a spectacular 100%. So then I read about exporting functions, but window['MyClass'] = MyClass will only export the constructor. To be honest, I'd rather not export every single method individually. I thought the compiler would export and not obfuscate all publicly available methods but those with a #private annotation.
What's the best way to teach the closure compiler to do that and not have to export every method individually?
Using ADVANCED_OPTIMIZATIONS you must export EVERY public method and property. If you do not want the public methods and properties renamed, then use SIMPLE_OPTIMIZATIONS.
See my Which Compilation Level is Right for Me post for more details.
I believe I found the answer: I can annotate methods with #export and run the compiler with --generate_exports. But maybe someone has an even better way.
I am having trouble wrapping my mind around primarily RequireJS. I see that it is a good/necessary technology, but implementing it, for me, has been a real stretch. I greatly appreciate your help!
I am trying to develop a fairly flexible application with Backbone and RequireJS. The problem is that I am totally used to syntax like new Person() without having to specify dependencies. Is there an efficient way to use RequireJS with quite a number of models? I think my problem is always working with returns. I considered using a factory method to create the model with the require function, but doing so necessitates that the require function is synchronous, which completely defeats the purpose of RequireJS.
It just doesn't seem right to have to require all of my models first and then include those in the instantiation function - or do I?
Do you have any suggestions or tutorials about how to structure and model an application like this?
Thank you for helping me out!
JMax
You can use what I call require js module pattern. If you know a group of classes are often used together you can do something like this.
First you define each class in a separate file and then you define a module to hold them together
Module.js
define([
'./models/FirstModel',
'./models/SecondModel',
'./views/FirstView',
'./views/SecondView',
'txt!./templates/template.tpl'
], function(FirstModel, SecondModel, FirstView, SecondView, template) {
return {
FirstModel: FirstModel,
SecondModel: SecondModel,
FirstView: FirstView,
SecondView: SecondView,
template: template
}
});
And then when you want to use class from this module you just do
define(['./Module'], function(Module) {
var AView = Module.FirstView.extend({
model: Module.FirstModel,
render: function() {
this.html(_.template(Module.template)(this.model.attributes));
if (something) {
this.$el.append(new Module.SecondView().render().el);
}
}
})
return AView;
});
I don't believe using modules defined with requirejs we should return an instance - we should always return a constructor or an object.
You should totally embrace the defining and requiring - with time you'll start to love it - without having to think much about adding/tracking dependencies etc. everywhere by hand or (so 2005!) having most of the stuff in one file :)