The most lowly newbie JavaScript programmer knows that JavaScript, for good or bad, is a dynamically typed language and that a variable can take on any value. Many attempts have been made to somehow rectify this situation so that type errors occur less often. A seasoned (I taste like seasoning salt) JavaScript programmer also knows that type errors are not actually that common. I do not intend to denigrate (much) any of those approaches here (TypeScript, JSDoc etc.) but instead I will make a case for a JavaScript specific Hungarian Notation. It you don't know what Hungarian notation is, here is the Wikipedia page...Hungarian Notation
Full disclosure; I have not used this in anything but small test programs but I have used Hungarian Notation before (when it was all the rage...was it ever all the rage?). I think it has two qualities that elevate it above other solutions.
- It gives immediate knowledge as to the type of a variable.
- It is low impact in regards to typing (both kinds).
Lets look at JavaScript types.
String
Number
Bigint
Boolean
Symbol
Object
Array
Date
Set
Undefined and Null are technically types but I will not consider them for this purpose. I intend to keep this system simple. For the most part I think the letter choices seems simple. Our major complication is String,Symbol and Set. We could use "c" for strings (characters) but I think we should stick with "s". Maybe sets should be "se". We don't prefix Symbols because maybe that doesn't make much sense? So here is what our prefixes will be. It's kinda weird that Bigint literals are suffixed with n. But then again Bigints are big, stupid and largely useless. Oh well, they didn't ask me.
Type | Prefix |
---|---|
String | s |
Number | n |
Bigint | i |
Boolean | b |
Symbol | N/A |
Object | o |
Array | a |
Date | d |
Set | se |
Let's give it a try. Here is a quicksort and I am using it to sort a string of letters.
let sNotWritingJustTyping = 'thequickbrownfoxjumpsoverthelazydog';
function swap(aItems, nLeftIndex, nRightIndex){
let vTemp = aItems[nLeftIndex];
aItems[nLeftIndex] = aItems[nRightIndex];
aItems[nRightIndex] = vTemp;
}
function partition(aItems, nLeft, nRight) {
let nPivot = aItems[Math.floor((nRight + nLeft) / 2)], //middle element
i = nLeft, //left pointer
j = nRight; //right pointer
while (i <= j) {
while (aItems[i] < nPivot) {
i++;
}
while (aItems[j] > nPivot) {
j--;
}
if (i <= j) {
swap(aItems, i, j); //sawpping two elements
i++;
j--;
}
}
return i;
}
function quickSort(aItems, nLeft, nRight) {
let nIndex;
if (aItems.length > 1) {
nIndex = partition(aItems, nLeft, nRight); //index returned from partition
if (nLeft < nIndex - 1) { //more elements on the left side of the pivot
quickSort(aItems, nLeft, nIndex - 1);
}
if (nIndex < nRight) { //more elements on the right side of the pivot
quickSort(aItems, nIndex, nRight);
}
}
return aItems;
}
// first call to quick sort
let sSorted = quickSort([...sNotWritingJustTyping], 0, sNotWritingJustTyping.length - 1).toString().replaceAll(',','');
console.log(sSorted); //prints abcdeeefghhijklmnoooopqrrsttuuvwxyz
"Wait!" you say. You notice a few things. What's with the "v"? Well sometimes we really don't know what a variable will contain so it is a variant type. What about function names and index variables? You could, if you wanted to go nuts with this, and denote what a function returns. Swap is a void function so it would return undefined so it would be uSwap...ah crap looks like we have to have undefined after all. Partition returns a number so it would be nPartition and quickSort would be aQuickSort. I am of two minds about this. I think published public interfaces and class names should not be Hungarian so as to not impose that style on a third party's code. I think the standard one letter indexing variables should just stay their good ole Fortran'y selves. So here is our new list
Type | Prefix |
---|---|
String | s |
Number | n |
Bigint | i |
Boolean | b |
Symbol | N/A |
Object | o |
Array | a |
Date | d |
Set | se |
Undefined | u |
Variant | v |
I think this method of naming helps with an issue that concerns programmers without introducing anything more complicated than prefixing variables and without yat (Yet Another Transpiler). I am also of the opinion that when possible, variables should be initialized with a value of their intended type even if that value is never used. Hungarian Notation and good initialization practice should be sufficient for other programmers on your project(s) to glean your intentions as to variable type.
Thanks for reading!
BTW I yoinked the quick sort from QuickSort Algorithm in JavaScript
Top comments (11)
Hi, thanks for your article.
Hungarian notation did not gain popularity when it was used to mark the type. It gained popularity when it was used to mark the intention of a variable. This was very useful when they developed Microsoft Word and wanted to make sure not to confuse document- and window-coordinates. - This is called "Apps Hungarian"
It lost popularity when it was adopted to other projects and used to mark the type. - This is called "Systems Hungarian"
The Wikipedia-Article on Hungarian Notation has all the details.
This article from '05 actually is a strong case for Hungarian notation. It is a good read, I recommend to every developer:
joelonsoftware.com/2005/05/11/maki...
IMO, it does not make sense to use Hungarian notation in the quick-sort example. In fact, it does not make sense to use Hungarian notation for types at all. (Except if you have a jQuery codebase that is splattered with
$
-prefixed$myElement
variables. Then you should keep them. So you can easily spot the messy places you need to clean up).Small JS Projects that are simple enough don't need Hungarian notation. Once the project becomes so complex it could actually benefit from Hungarian Notation, it is justified to use JSDoc or even TypeScript.
Quicksort was included merely for example purposes and not as an example of a real world application that would include many js files and requisite imports.
I think the jQuery element =
$
convention is useful beyond knowing where to clean up code. I frequently use cheerio (a modern, server-side library with an API based on jQuery's) for working with XML and HTML data, and I frequently want to have a similarly-named variable in scope for both an element and its underlying data. For example:It can also be useful when used more loosely to represent DOM elements when you're interacting with them directly:
Back in the day, the approach was very common on the Windows-family platform. A sample from a best-seller:
Programming Windows 3.1 by Charles Petzold - HelloWin.c
It's nice to remember that it existed but I believe it's better to leave Hungarian notation where it belongs: 40 years in the past 😅
You can use JSDoc to accomplish the pretext of this post; See here.
As you can see it offers much more of what Hungarian notation can offer: error checking in dev time, real type information, ability to define types...
It’s easy to build a type checking tool for Hungarian notation, np 🏃🏻♂️🤣
😂😂
Interesting approach indeed, never met it in real life but can imagine it making sense for some projects. Looks ugly but efficient… kinda like tailwind 😄
I'm not sure it enhances readability, but it would allow to implement a universal type check routine. Nice Idea anyway....
I tried to build a type check routine for Hungarian notation, but this was not as easy as I thought. You need to access the variable names inside a function, which seems to be a very special job in Javascript (If I missed something, please provide some code example).
Anyway, your post inspired me to come up with a slightly different solution, which is very nice and exactly what I've been looking for for a long time. See this post for more information
Thanx for sharing it. It was helpfull. This notation can really be helpfull.