What I Learned at Work this Week: Why I Was Wrong About That Webpack Plugin

Photo by Viktoria Goda from Pexels

About a month ago, I wrote a blog post about using webpack to solve for ES6 functions not being supported by Internet Explorer. In the post, I explained that functions like find would throw an error in IE because they are not natively programmed into the browser and that I was hoping to use a webpack plugin to remove find from my code as it transpiled. The post went over configuring webpack and ultimately got the plugin working, but if we were using .find on an array saved as a variable, this was the result:

Array.isArray(arr) ? arr.filter(v => v === 1)[0] : arr.find(v => v === 1);

I wrote that this would still result in an error and that I had to find an alternate means of transpiling my code. This week, I learned that this actually isn’t true. If I was still getting an error after my code was transpiled in this way, it was coming from somewhere else. As it turns out, JavaScript wasn’t working exactly as I thought it was.

As I continued to investigate my issue, I sought to learn what actually happens when we attempt to run ES6 functions on Internet Explorer. I was under the impression that a script with any unsupported functions would fail immediately, before any logic was run. But while testing, I noticed something unusual — some functionality of the scripts was actually working.

IE simulated using BrowserStack

We can’t really tell what’s happening from this image here, but what we can see is critical: a network call is being made by my company’s script. This is a script that I know for a fact contains find and includes. And the logic that requires those functions did indeed fail. But the script itself was transpiled and loaded without issue. I realized that I had some misconceptions about what was actually going wrong with my code. So I sought to learn what exactly happens when IE attempts to run find.

My first step was Google, which provided plenty of helpful advice on how to solve issues of ES6 in IE, but no explanation for the root cause of the issue. So I’ll type this again in case anyone else is trying to find this answer on the internet: What happens when I use ES6 on Internet Explorer?

I was finally able to learn the answer thanks to a very friendly and helpful Senior Engineer who works on the Frontend Team. He explained that the reason our code can work even if it includes unsupported functions is that these functions do not cause compilation errors. Every browser reads JavaScript thanks to an engine, responsible for parsing, interpreting, and compiling the code into something that can be quickly and easily read by a virtual machine. Each browser runs on its own engine, and some of them support a syntax that others don’t. Firefox, for example, knows what import means in JavaScript and can compile code with that keyword without issue. Internet Explorer, however, does not. An attempt to run code that uses import on Internet Explorer will result in a compilation error — the code will not finish compiling and therefore no part of it can be read and executed by the browser. This is why Babel and other transpilers transform

import module_name

to

require(module_name)

If my script contained find but some part of it worked, this must mean that attempting to invoke find does not cause a compilation error that will tank our code. The important distinction here is that import is its own statement, but find is a property of a pseudo-class (Array). In other words, JavaScript has defined a pseudo-class called Array that has certain properties as part of its prototype. Among those are length, indexOf, and as of ES6, find. The fact that IE doesn’t support find simply means that its engine does not natively provide that property as part of Array.prototype. The good news for us, however, is that these property lookups are done in runtime rather than compile-time, which means attempting to invoke Array.prototype.find or String.prototype.includes won’t immediately break my script. The browser engine doesn’t have a problem compiling arr.find because it has not yet checked whether arr’s type is Array and whether Array.prototype includes a property called .find.

This finally made sense of what I was seeing in the browser. The parts of the script that did not require find or includes were working. Only once I actually triggered that logic did I see a console error. And even then…I didn’t always see an error.

I thought IE didn’t have an Array.prototype.find value?

This is one of my absolute favorite parts of this saga because it is still lacking a full explanation. This screenshot was taken while simulating an IE environment. In the console, we can see that Array.prototype.find and String.prototype.includes are both defined. Per MDN, that is not the case by default. Rest assured, the emulator hasn’t added them in because this experience isn’t consistent across all sites. Instead, my expectation is that another script on this site has written a polyfill that populates the prototype (at least for Array — I have no explanation for why String.polyfill.includes appears to be native code). Once that script runs, any subsequent references to Array.prototype.find will execute the polyfilled code! On this particular site, my company’s code worked perfectly because someone just happened to write that polyfill.

This was tremendously exciting for me because I have written about polyfills, prototype, and IE support and now the knowledge I had gained was all coming together. I could understand why a polyfill would provide a prototype with a new property if that property did not previously exist. I understood that the IE error said ‘Object doesn’t support property or method “.find”’ because JS moves up the prototype chain when searching for a defined property. And now I finally understood why using a polyfill to update the prototype could essentially save all of my code with a simple import. My hope is this will make sense to loyal readers as well, because that’s what this blog is all about!

The webpack plugin I originally tried didn’t use a polyfill. Instead, it transpiled the code to include a filter option (which is supported by IE). Let’s look at the result again:

Array.isArray(arr) ? arr.filter(v => v === 1)[0] : arr.find(v => v === 1);

I had said that this would be a problem because it still included find. And it’s true that if that part of the code ever has to run, it will fail in IE. But the logic here states that if our variable is an array, we will use filter to iterate through it and search for our desired value. If that’s the case, find will never be run, IE will never have to check up on a prototype, and we’ll never see an error in our console. That’s the difference between a compilation-time error and a runtime error.

And I think that this case illustrates the difference between feeling overwhelmed by a problem and trying to break it down piece by piece. I first started researching polyfills in November and then gave prototype a shot a week later. In both of those posts, like in every one of my posts, there were things that I simply couldn’t understand or explain. Still, I considered it a win that I had gained some knowledge and held out hope that it would come in handy in the future. Now, four months later, those posts were the building blocks of solving a problem made simple by my understanding of JavaScript and general programming principles. I hope that every reader will have the opportunity to experience that as well.

This post was based on knowledge I gained while researching these three previous posts:

Solutions Engineer