Why was my javascript code not accepted at interview? - javascript

The interviewer told me that I have to follow javascript "patterns" and write "clean code". He also said that I should follow the prototype pattern. Here is my code sample:
//namespace declrations
var MyNamespace = {};
MyNamespace.UCs = {};
MyNamespace.Pages = {};
//function declarations
MyNamespace.UCs.test = function () { alert('this is a test function in user control namespace.'); }
MyNamespace.Pages.test = function () { alert('this is a test function in web page namespace.'); }
So can anybody point me to why this code is not ok? I mean, I have declared namespaces first and then added my members and functions like the above sample. So does it really have issues or am I missing something?

Well when you are writing code in a large environment, lots of problems can start happening. So it's important to separate your class definitions from how you use those classes. Which also means you have to make classes that can be unit tested to prove that they do what you say they do. Javascript is not a true object orientated language and as such there are several ways to "fake" it. But because the language has a lot of flexibility, we can duplicate some approaches.
One thing we want to stay away from is something called function scope simply because it can cause unintended "features" later down the road when 3 or 4 other programmers start making assumptions about what your code is doing. If they don't know a global variable was overwritten one or two function closures ago, it will make finding that problem more difficult. So I would suggest using a small class created by John Resig as it provides a very simple approach that gives you alot of the functionality you need.
So let's write some code.
var myNamespace = myNamespace || { }
/**
* Used to store a single entry in the log
*
* #class
*/
var myNamespace.LogEntry = Class.extend({
/**
* Used to track the beginning of the page load
*
* #private
* #static
*/
PAGE_LOAD_TIME = new Date().getTime(),
/**
* Used to store the current time
*
* #type int
*/
time : undefined,
/**
* The message of this log entry
*
* #type string
*/
msg : undefined,
/**
* #constructor
*
* #param {string} msg The message of this log entry
*/
init : function (msg) {
this.time = new Date().getTime() - this.PAGE_LOAD_TIME;
this.msg = msg
},
/**
* Displays this log entry in a single string
*
* #return {string} String representation of this log entry
*/
toString : function () {
return this.time + ": " + this.msg;
}
});
/**
* Used to store a log entry that has data associated with it.
*
* #class
* #extends myNamespace.LogEntry
*/
var myNamespace.DataEntry = myNamespace.LogEntry.extend({
/**
* Used to store data associated with this log entry
*
* #type object
*/
data : undefined,
/**
* #constructor
*
* #param {string} msg The message that describes this log entry
* #param {object} data The data associated with this entry
*/
init : function (msg, data) {
this._super(msg);
this.data = data;
},
/**
* #return {string} The string representation of this log entry
*/
toString : function () {
// Uses a JSON library to stringify the data into a json string.
return this._super() + JSON.stringify(this.data);
}
});
/**
* Provides an interface to log messages
*
* #class
*/
var myNamespace.Log = Class.extend({
/**
* Stores log entries
*
* #type myNamespace.LogEntry[]
*/
log : undefined,
/**
* #constructor
*/
init : function () {
this.log = [ ];
},
/**
* Logs a message into the log
*
* #param {string} msg The message you want to log
*/
msg : function (msg) {
this.log.push(new myNamespace.LogEntry(msg));
},
/**
* Log a message and data into the log
*
* #param {string} msg The message of this log entry
* #param {object} data The data associated with this log entry
*/
data : function(msg, data) {
this.log.push(new myNamespace.DataEntry(msg, data));
}
});
Ok, there is lots of stuff going on here. The main part is that this is all definitions of classes. I don't actually use anything up there. The program only stores the current time in LogEntry.PAGE_LOAD_START which is declared #static so the behavior will be expected. I've used lots of jsDocs here to make everything clear about what the intentions are. A program like intelliJ can use those to give your code feedback if you aren't using the classes the way you've documentated them.
This program will let you create and store log entries with possibly logging data. There are alot of other ways to do this. I declare everything before the constructor instead of inside the constructor so that I can document the types and whether they are private.
A programmer that has to use the log will know exactly how to use it and if they want to create or extend these classes, they can do so without unintended effects from function closure.
Here's how to use it:
var anotherNamespace = anotherNamespace || {};
var anotherNamespace = new myNamespace.Log();
...
anotherNamespace.log.msg("This is a test");
...
anotherNamespace.log.data("Test msg with data", data);
Of course the obvious thing missing here is a way to display all the data. But that could be in another class that iterates through the Log.log array and spits out the toString() into a web page or file. The point here is that the classes and their functions are simple, unit testable, and definition only.

FIrst of all, make use of object literals, all those assignments are a waste lines.
Second, you don't have the prototype "pattern" implemented anywhere, also I'd go for some encapsulation if you have full control over the namespaces:
(function() { // anonymous wrapper
function Foo(a) { // i can haz prototype "Pattern"
this.memberVal = a;
}
Foo.prototype = {
someMethod: function(value) {
console.log(value, this.memberVal);
},
yam: function() {
}
};
// Let's assume we have full control here, otherwise Alnitak's answer is fine too.
window.namespace = {
test: {
Foo: Foo
}
};
})();
var test = new namespace.test.Foo(123);
test.someMethod('bla');

Maybe the interviewer literally meant you should be using the 'namespace' method?
Tutorial with example of usage:
http://elegantcode.com/2011/01/26/basic-javascript-part-8-namespaces/

Your code looks (mostly) fine, if the intent was to just put utility functions in a shared namespace, without any object orientation or data encapsulation.
I would have made one change, on the initial name space declarations:
var MyNamespace = MyNamespace || {};
MyNamespace.UCs = MyNamespace.UCs || {};
MyNamespace.Pages = Mynamespace.Pages || {};
to ensure that the code didn't obliterate any existing methods in those namespaces.

Related

How to define a static variable inside a JS class

I'm building a static class to store data inside an a array and i want to declare a static variable but i don't know how to do it on javascript.
JS code:
class GCache {
// Define cache variable
static cache = {}; // <--- that is what i don't know how to do
/**
* Check if data is on cache
* #param {*} id
*/
static isOnCache(id){
return this.cache.hasOwnProperty(id);
}
/**
* Add data to cache
* #param {*} id
* #param {*} json
*/
static addToCache(id, json){
if(this.isOnCache(id)) return;
this.cache[id] = json;
}
/**
* Obtain data from cache
* #param {*} id
*/
static getFromCache(id){
if(this.isOnCache(id)) return;
return this.cache[id];
}
}
Thank you <3
In up-to-date JavaScript environments, you can use the static keyword when inside a class to assign properties to the instance itself - the code in your question works fine now.
class SomeClass {
static someStaticProperty = {}
}
console.log(SomeClass.someStaticProperty);
In older environments, and at the time the question was originally posted, non-function properties couldn't be added to a class itself inside the class definition. It looks ugly, but you'll have to assign the property outside of the class definition:
class GCache {
...
}
GCache.cache = {};
Also note that your getFromCache function probably has a bug: you probably want to return early if the id being searched for does not exist in the cache:
if(!this.isOnCache(id)) return;
class GCache {
/**
* Check if data is on cache
* #param {*} id
*/
static isOnCache(id){
return this.cache.hasOwnProperty(id);
}
/**
* Add data to cache
* #param {*} id
* #param {*} json
*/
static addToCache(id, json){
if(this.isOnCache(id)) return;
this.cache[id] = json;
}
/**
* Obtain data from cache
* #param {*} id
*/
static getFromCache(id){
if(!this.isOnCache(id)) return;
return this.cache[id];
}
}
GCache.cache = {};
GCache.addToCache('myid', 'data');
console.log(GCache.getFromCache('myid'));
But, in this case, it would probably be easier to use a plain object, rather than a class. The class isn't being used to instantiate anything, after all, and with an object, you can both define cache inside the object, and reduce the syntax noise by getting rid of all the statics:
const GCache = {
cache: {},
isOnCache(id) {
return this.cache.hasOwnProperty(id);
},
addToCache(id, json) {
if (this.isOnCache(id)) return;
this.cache[id] = json;
},
getFromCache(id) {
if (!this.isOnCache(id)) return;
return this.cache[id];
}
}
GCache.addToCache('myid', 'data');
console.log(GCache.getFromCache('myid'));
There is currently a proposal allowing you to set static non-method properties onto a class. It's currently at Stage 2, which means it's expected to eventually be implemented officially. Once it lands, the code:
class GCache {
...
}
GCache.cache = {};
can be replaced by:
class GCache {
static cache = {};
...
}
If you mean static variable for all instance of the class,
declare:
GCache.cache = {}
below your class declaration
Babel's plugin-proposal-class-properties is probably what you're looking for. It enables support for static variables/methods inside the class declaration.

JSDocs - How to document implicit parameters and returns

I am wondering how to document a function like the following:
var createInput = function () {
var type = this.type;
this.input = {
val: {},
type: type
};
}
Technically this.type is an input parameter and this.input is what is returned from this object. How would one document this?
Riffing on the second link by sapithpocker seems to show a potential solution.
/**
* The context of the current class.
* #typedef {Object} ClassContext.
* #property {string} type - Indicates type. */
/**
* A method for doing something.
* #param {...ClassContext} this - a {#link ClassContext} object.
*/
function createInput() { /* Code */ }
This is documentation that can be reused for each method the uses object that "owns" the current context, i.e. this, and follows the DRY-principle, as well.
It is better to rewrite the code someway so that it takes arguments and returns some value without any side effects. Easy to document as well!
The question should be more like how to avoid side-effects, rather than how to document side effects.
var createInput = function (type) {
return {
val: {},
type: type
};
}
this.input = createInput(this.type);

WebStorm type checking with type defined in an IIFE

I often define a class-like data/type structure in js, such as:
/**
* #param {string} d
* #constructor
*/
function MyType(d) {
/**
* #param {string} a
* #param {string} [b]
*/
this.search = function (a, b) {};
}
I use jsdoc to document my types, and WebStorm / PhpStorm / IntelliJ usually does a great job with autocomplete and warning me if if I forgot a function arg etc...
But, this doesn't work so well when I define my type inside of an IIFE, like so
var MyType = (function () {
/**
* #param {string} d
* #constructor
*/
function MyType(d) {
/**
* #param {string} a
* #param {string} [b]
*/
this.search = function (a, b) {};
}
return MyType;
})();
The problem is I won't get proper warnings when I forget function args. For example the following should give me squiggly lines/warnings for both lines, letting me know I'm missing arguments.
var mt = new MyType();
mt.search();
It works perfectly if I don't define my type inside the IIFE, but I can't make it work perfectly with it. I've tried a few different things, which seem to have various levels of success, but none work perfect.
If I do
var MyType = (function () {
...
return MyType;
})();
var mt = new MyType();
mt.search();
...the problem is I don't get warned about missing a constructor arg, although it does correctly warn me about the missing search() arg. Also, Ctrl + Click on MyType takes me to the var MyType line, when ideally it should take me to the orig function definition.
If I do
/** #var {MyType} window.MyType */
window.MyType = (function () {
...
return MyType;
})();
var mt = new MyType();
mt.search();
...then I do get proper warnings about missing constructor arg, and Ctrl + Click on MyType works properly, by I don't get a warning about missing args on the .search() call, and Ctrl + Click on search doesn't work right.
Is there a way to make this work better when using IIFE's? Ideally, I'd prefer a method that doesn't resort to global state/definitions.
The issue is tracked as WEB-20246; please follow it for updates

Nested Methods in sidebar of JSDoc

Thanks to the answer found here:
https://stackoverflow.com/a/19336366/592495
My JavaScript documentation is well-organized and well-formatted. Each namespace is a "parent" of methods contained within. However, navigation is not quite as granular as I would like.
After compiling/rendering using the node.js tool via a simple command (jsdoc file1.js file2.js), the docs are generated into a default template. This default template displays my namespaces in the sidebar navigation, but it does NOT show methods that each contains.
You can fake a list of methods by adding the #class directive to each method, but as we know, they are not really classes.
I would love to see a sidebar navigation like this:
My Project
- namespace 1
- method.a
- method.b
- method.c
-namespace 2
- method.d
- method.e
Any direction to documentation I have overlooked would be greatly appreciated.
[edit to add:]
Upon experimentation, #class does almost exactly what I want but with some exceptions:
It lists classes above namespaces. I don't like that since the namespaces are the "parents" as it were.
JavaScript doesn't have classes in that sense. Not ones that are called "classes" with that nomenclature. It creates a weird disconnect when reading the document to see a list of "classes".
It adds the "new" operator automagically. Not all of the methods have constructors... you can see the problem!
[edit: sample code]
So here's the current structure. Before I annotate it with JSDoc comments, here's the basic approach:
var app = app || {};
app.utils = {
whizbang: function() {},
geegolly: function() {}
};
app.render = {
thestuff: function(params) {},
thethings: function(params) {}
}
}
So, using object literal notation, the top level is a "namespace" for the whole application, but within there are sub-namespaces for different purposes. Here, I have a sub-namespace specific to utilities, and another one specific to rendering. Each can have properties, but more importantly they each contain functions. It is these functions which should appear in the sidebar. Now to flesh it out with my current pattern for JSDoc:
/**
* #file MyApp.js This is an awesome description of MyApp.js
*
* #version 0.1
* #author Greg Pettit
* #copyright 2015
*
*/
/**
* Description of my main namespace!
*
* #namespace app
*/
var app = app || {};
/**
* This is a description of my sweet utilities namespace!
*
* #memberof app
* #type {object}
* #namespace app.utils
*/
app.utils = {
/**
* app.utils.whizbang is an awesome function with magical powers. I sure wish
* it would appear in the sidebar as a member of app.utils!
*
* #memberof app.utils
* #method whizbang
*
* #param {method} [successCallback] Method invoked on successful attempt.
* #param {method} [errorCallback] Method invoked on unsuccessful attempt.
*
*/
whizbang: function(successCallback, errorCallback) { // do utility stuff! }
}
/**
* This is a description of the best rendering namespace ever.
*
* #memberof app
* #type {object}
* #namespace app.render
*/
app.render = {
/**
* app.render.thethings renders the things! I wish it would render to the sidebar...
*
* #memberof app.render
* #method thethings
*
* #param {method} node The node to which thethings are rendered
*
*/
thethings: function(node) { // do rendering stuff! }
}
Have you tried using the #lends tag? An example of your code and doc comments would be helpful here.
Since I don't know what your code looks like, I'll just give an example of how I use JSDoc with our in-house framework, which has lots of idiosyncracies (hey, I didn't write it, I just have to use it).
Just to give some context, we have a context object that can create apps and modules (apps are just modules with a start method):
/** #namespace MyApp */
var MyApp = context.app('myApp').use('module1', 'module2', 'underscore');
We have a dependency injection system for backbone that uses an angular-style pattern for expressing dependencies:
/**
* The constructor for MyModel
* #memberof MyApp
* #param {object?} attrs
* #param {object?} options
* #param {AppUtils} appUtils
* #constructor
*/
MyApp.MyModel = function(attrs, options, appUtils) {
this.options = options;
this.utils = appUtils;
}
// This is injected by the dependency resolver at instantiation time
// No additional JSDoc comments are necessary, it actually gets this right
MyApp.MyModel.prototype = {
idAttribute: 'customId',
defaults: {
customId: '',
name: '',
birthday: null
}
};
// Registers MyModel with the IOC container as 'myModelName'
MyApp.model('myModelName', [
'attrs',
'options',
'appUtils'
MyApp.MyModel
]);
And then a different file can have an instance of myModelName injected by adding it to that dependency array at the bottom.
Funny thing is, JSDoc actually does a pretty good job of understanding that particular arrangement, as long as I don't try to get too fancy... but the following pattern is apparently too confusing for it:
/**
* #memberof MyApp
* #param {MyApp.MyModel} myModel
* #param {urlParser} urlParser
* #constructor
*/
MyApp.SomeService = function(myModel, urlParser) {
return {
foo: function() {
//whatever
},
bar: function() {
//whatever
}
};
};
MyApp.service('someModuleName', [
'myModelName',
'urlParser',
MyApp.SomeService
]);
The only thing that I've found that gives me anything close to the desired output is using the #lends tag to tell JSDoc that a particular object/property/method is "lended" as a different property. For example, to document the attributes property of a backbone model (which is ostensibly defined by its defaults property), we do this:
MyApp.MyModel.prototype = {
idAttribute: 'customId',
/** #lends MyApp.MyModel.prototype.attributes */
defaults: {
customId: '',
name: '',
birthday: null
}
};
And for that case where the service is returning an object, the only way we've found to get those object properties documented is like this:
/**
* #memberof MyApp
* #param {MyApp.MyModel} myModel
* #param {urlParser} urlParser
* #constructor
*/
MyApp.SomeService = function(myModel, urlParser) {
/** #lends MyApp.SomeService.prototype */
return {
foo: function() {
//whatever
},
bar: function() {
//whatever
}
};
};
I have no idea if any of that was useful, but maybe it'll give you some ideas for things you could try with #lends. If you can provide some example code, I can possibly give you a more useful answer.

jsDoc Toolkit methods, fields and properties don't show when I use a self var

Toolkit
I'm trying to document my classes but i'm using a "self" var to expose only the public methods.
jsDoc can find the class name, but can't find the methods, fields, properties, etc...
this is 1 of my classes:
Anny suggestion how I should approach this?
(function(App){
/** #class The ViewModel for the EventView */
App.ViewModels.EventsViewModel = function(service) {
var self = {};
/** Observable array containing events */
self.events = new ko.observableArray();
/** Call the fetchEvents method on the service */
self.refreshEvents = function(e){
$('.refreshBtn').changeIcon('refreshing');
service.fetchEvents();
}
/** subscribe on the service->currentEvents var
* on change update the events in this viewmodel
* set the refesh butting is set to refresh (instead of refreashing) */
service.currentEvents.subscribe(
function(newValue){
self.events(newValue);
$('.refreshBtn').changeIcon('refresh');
}
);
/** function for a timespan string ex: "10:00 - 14:00"
* Date inputs should be of ISO-8601 Date format */
self.toTimeString= function(/* String */ start,/* String */ end)/* String */
{
var out = "";
try
{
out =(start!=null && end!=null)? Util.IsoDateParse(start).format("HH:MM") + " - " + Util.IsoDateParse(end).format("HH:MM") : ""
}
catch(err)
{
out ="Error during toTimeString.\n\n";
out+="Error description: " + err + "\n\n";
}
return out;
}
/** Call the fetchEvents method on the service */
self.refreshEvents();
return self;
};
})(App)
ps: I'm using Knockoutjs & jQueryMobile
EDIT:
Thanks! Almost there... I tried to do something like this:
/** #memberOf App.ViewModels.EventsViewModel#
* #field * #description Observable array containing events */
self.events = new ko.observableArray();
jsDoc shows it like "self.events" instead of "events"
Use the memberOf tag(TagMemberOf). And since App.ViewModels.EventsViewModel is declared in an anonymous function you may need to use the name tag(TagName) to scope it as global.
Edit:
Try:
/** #memberOf App.ViewModels.EventsViewModel#
* #field
* #description Observable array containing events
* #name events
*/
self.events = new ko.observableArray();
It is a simple constructor.
Use 'this' instead 'self'.
JsDoc understand members of 'this'
If you want to use other name than 'this', you can add alternative
var StackOverflow = function(){
/** Alternative */
var me = this;
/**
* Default rating
* #type {number}
*/
this.rating = 10;
/** Load Happy New Year: increase rating */
this.loadNewYear = function() {
$.ajax().done(function(){
me.rating = 100500;
});
};
};
PS: do not use 'self': https://stackoverflow.com/a/3216464/1197421
Use 'me' or something else.

Categories