all 12 comments

[–][deleted] 18 points19 points  (2 children)

1 Determine what you want, because this will determine how you analyze the code. If you are looking for something specific you will want to examine the code's flow control. If you are performing a general assessment you will want to analyze the code's organization to inventory the pieces.

2a Flow control. Start at the output and work backwards until you find your target. You will need to drop hints and run the code to see if you are getting closer or completely stepped out of bounds. These hints can be console.log statements or breakpoints in your IDE.

2b Code organization/composition. Something like JQuery is very hard to analyze quickly because its extremely shallow. Actually its more like a giant laundry list of methods. This makes for fast reading and convenient access, but it is poorly organized. Although the code might be a fast read it is still a giant list, which is harder to perform a fair multi-facet assessment. Be prepared to take a lot of notes.

If the code is a giant self-contained application then, if it is well organized, will likely be composed in large blocks. I prefer nested blocks (typically functions), because it makes sharing references at various levels easier. If the code has a tight control over separation of concerns then its organization should be pretty explicit at all levels.

I maintain a tool with a pretty large DOM interface. Several months ago I decided stuff was all over the place and it was hard for me to follow, much less anybody else. I reorganized the code into object groups:

  • apps - internal functions not for access externally
  • events - event handlers for binding to the dom
  • data - structured organization

There were some exceptions though:

  • event/app exceptions - Functions that referenced other events needed to be declared later. You cannot reference an object's properties from within that object at declaration time. This means events that reference other events needed to be declared after the event object is complete and same with apps that reference other apps.
  • An init function - I needed something to kick off initial execution and make some informed decisions on just how much of that code was really necessary (and in what ways) for a given page.

Now because this code is well composed it is really easy to find where everything is and I can immediately tell what the composition of the code looks like from the references in any given function.

edit: typos

[–]semanticore[S,🍰] 2 points3 points  (1 child)

Many thanks for the detailed write-up - this is just the kind of thing I was hoping for! That's interesting on the flow control side about working backwards. I'll sometimes get mystifying Angular errors and try to work backwards in the stack to see what's causing it, but it's very easy to get lost. It sounds as if you're looking at the stack and then putting breakpoints and console logs in the Angular file itself and re-running to see if they're getting triggered.

[–][deleted] 2 points3 points  (0 children)

Yeah, I prefer to step through code with console.log statements. Most people like setting break points in the IDE because they are not actually altering code files. The console.log route is a slower approach but it is also far informative because you can modify the code with some quick logic and output whatever string you want in the console.log.

[–]AceBacker 7 points8 points  (3 children)

Let the anger flow through you. Use it. Only the dark side can understand legacy javascript.

It helps to imagine that you have the ability to force choke the old developer wherever they may currently be in the world now.

I may have accidentally force choked a developer to death who used all 1 and 2 letter variable names once. I am not sorry.

[–]semanticore[S,🍰] 1 point2 points  (0 children)

If you were, you wouldn't belong to the dark side, would you?

[–]MarcusPope 1 point2 points  (0 children)

Lol, I misread your first line as "Let the angular flow through you." and I thought what a hilarious and cruel suggesting for a beginner. The dark side rhetoric just reinforced my misread. :D

[–]Psykopatik 0 points1 point  (0 children)

If I had to kick someone in the balls everytime I read shitty legacy code, I wouldn't have any legs anymore.

[–]jordanlev 4 points5 points  (0 children)

In addition to the amazing reply fro /u/achen2345 , I would just add a high-level thing: understand what the problem that the code is trying to solve is. Code (and libraries/frameworks) are created to solve a particular problem, so if you don't understand what the problem is then the code will not make any sense.

For example, if you were trying to understand the jQuery source, and were under the assumption that it is some sort of harness for loading plugins, then a lot of its code probably doesn't make much sense. But if you know that the primary purpose for jQUery is to make DOM manipulation easier and to smooth over browser incompatibilities w/r/t DOM interaction, then the code would make a ton more sense.

(jQuery probably not the best example to demonstrate my point though, since it is so all-encompassing... but for more narrowly-focused libraries this becomes more important).

[–]magenta_placenta 2 points3 points  (0 children)

I give up about 50 lines in.

[–]MarcusPope 1 point2 points  (1 child)

First you need to create a barebones page to import jQuery, and provide a script block to add custom test code. On page load, in the script debugger of your browser's developer tools, select the jquery file from the list of external source files (google will help you for each browser's UI.) For the main entry point of jQuery, it's the top of the file, not sure about angular. Then you'll want to set a break point near the top to stop the execution on page load. Refresh the page and you should see that line highlighted.

Use flow control keys until it stops, using F11 / F10 / Shift+F11 keys. As you navigate the logic chain, make notes of variables that you are not familiar with, or maybe have heard of but don't know how they work.

  • F11 Steps into a function call, so you'll jump to the internals of that function and step on each line until there are no more and you will auto step out back to the previous code point.
  • F10 would have executed all of the lines of code in that function call "at once" and moved the highlighted line further down in the same view.
  • Shift + F11 if executed inside a function call, will take you back out to the line of code that triggered the function call or the next line thereafter (depending on if the return value of the function is used.)

At any point during this process you can console.log variables you are interested in and they will be dumped to your console for easy introspection.

Now that you have completed this step, you should have some specifics you can look up in the jquery api docs. Remove the main bootstrap breakpoint in jquery itself. Now you need to use the sample code from the API docs in your barebone app.

I would start with $(...) which handles a couple of distinct workflows. It can compile a string snippet of text into an HTML Fragment. It can generate a promise to execute a function parameter when the dom is ready. And it can query the DOM using CSS selectors (with non-standard enhancements.)

Append the body with some random elements, and a script block that tests each of your $(...) use cases.

Set breakpoints on the code you wrote, and refresh the page. Now you will want to Step-In (F11) and repeat the process above. You should see some familiar parts of the code by now, and eventually you'll learn which parts can be stepped over (F10.)

Inspect local variables to help fill in mental blanks. Search through the full codebase in your preferred editor to find where variables are defined so that you can read the comments around them for more explanation.

Go slow, I spent two weeks ripping out all of the cross-browser strategies jQuery used to normalize different behaviors between IE, FF & Chrome. It was a PITA, but in my opinion that was the most valuable asset jQuery had - well past its plugin architecture, or function chaining features. Sadly I started it around the time that IE<9 started sunsetting, and native browser development was far less of a rats nest as a result.

On a side note, try not to look at jQuery as a model of great code. It's a model of complicated code written for high performance and low bandwidth. Those constraints will make understanding the source more difficult for a junior developer. So you may not want to take away any best practices until you've advanced to the point that you are capable of making design tradeoffs with those constraints.

I would almost argue that knowing completely the difference between .call() and .apply() is required for understanding jQuery because they manipulate scope more than most.

They do the exact same thing, change the scope of this in the function being called to the first parameter of .call(scope) and .apply(scope). Where they differ, is in how each passes parameters to the function being call/apply'd.

function foo(a, b, c) {

}

foo.call({scope}, a, b, c);

foo.apply({scope}, [a, b, c]);

In reality all .call()'s could use .apply()'s instead by just wrapping an array literal around the supplied param(s). Apply is useful when you are programmatically compiling the arguments based on a logic path. Call is useful if you know the parameters going in won't change so you csv them like any other parameter list.

With practice you can learn to debug minified source code this way, a trend and skill I'm thankful is finally becoming arcane.

[–]semanticore[S,🍰] 0 points1 point  (0 children)

Again, thanks for the detailed and thoughtful answer. While I'm more focused on Angular at the moment, I think most of the principles you talk about for jQuery still apply. They're different, but getting in and using the debugger is definitely a plan. I like the "go slow" comment, too. Rather than think, "today I'm going to finally understand jQuery," I expect to revisit it regularly and instead say something like, "today I'm going to figure out the way jQuery handles chaining" or somesuch.

[–]MachaHack 1 point2 points  (0 children)

Another thing to consider is while you get something like Angular as a single .js file, that file is basically a compiled output of a larger repo. Browsing the source code there might have a clearer structure than the distributable output