<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[JavaScript in Plain English - Medium]]></title>
        <description><![CDATA[New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers. - Medium]]></description>
        <link>https://javascript.plainenglish.io?source=rss----4b3a1ed4f11c---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>JavaScript in Plain English - Medium</title>
            <link>https://javascript.plainenglish.io?source=rss----4b3a1ed4f11c---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 01 Oct 2026 02:36:27 GMT</lastBuildDate>
        <atom:link href="https://javascript.plainenglish.io/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Dating App Development Cost in Australia: 2026 Guide]]></title>
            <link>https://javascript.plainenglish.io/dating-app-development-cost-in-australia-2026-guide-b55bbd189f60?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/b55bbd189f60</guid>
            <category><![CDATA[sydney]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[perth]]></category>
            <category><![CDATA[dating-app-development]]></category>
            <category><![CDATA[australia]]></category>
            <dc:creator><![CDATA[Sundeep Mann]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 15:06:14 GMT</pubDate>
            <atom:updated>2026-09-30T15:06:13.479Z</atom:updated>
            <content:encoded><![CDATA[<h3>How Much Does It Cost To Build A Dating App In Australia?</h3><figure><img alt="dating app development company" src="https://cdn-images-1.medium.com/max/1024/1*DNVEB4s5U2YbUjAocw8Uwg.png" /></figure><ul><li>A dating app in Australia typically costs between AUD 15,000 and AUD 300,000, depending on features and team.</li><li>Feature scope, team location and platform choice decide most of the price.</li><li>Hosting, moderation and maintenance add 15% to 25% of the build cost every year.</li><li>The cheapest quote often becomes the most expensive build once fixes start.</li><li>A phased build with a fixed scope per phase is the safest way to control the budget.</li></ul><p>Dating app development cost is the first question every founder asks, and the honest answer is a range. A simple app with profiles, swipes and chat is a different build from one with AI matching and video calls. This guide gives you Australian numbers, explains what moves them, and shows where budgets quietly blow out.</p><h3>Dating app development cost by complexity</h3><p>Most projects fall into three bands. These figures assume a blended team billing in Australian dollars, covering both iOS and Android.</p><p>A basic MVP has sign-up, profiles, photo upload, swipe or browse matching, in-app chat, push notifications and an admin panel. Expect AUD 15,000 to 50,000 and three to five months. The low end usually means a tight feature list and a cross-platform build.</p><p>A mid-level app adds video profiles, in-app calls, filters, subscriptions, verification badges and analytics. Budget AUD 50,000 to 150,000 over five to eight months.</p><p>An advanced app adds AI-driven matching, behaviour-based recommendations, icebreaker suggestions and photo safety checks. That means AUD 150,000 to 300,000 and eight to twelve months.</p><p>To see how a specific feature set prices out, this breakdown of the <a href="https://7pillars.com.au/blog/cost-to-build-dating-app-like-hinge/">dating app development cost</a> for a premium relationship-focused product is a useful reference point.</p><h3>What drives dating app development cost up or down</h3><p>Four things move the number more than anything else.</p><ol><li>Feature scope. Every extra feature (video, voice notes, stories, events) adds design, build and testing time. Cutting three features from a first release can save 20% or more.</li><li>Team location. Australian agencies typically charge AUD 120 to 200 per hour. Offshore rates are often AUD 40 to 80. Many founders split the work, with strategy and design close to home and engineering delivered by a larger team.</li><li>Platform choice. Two separate native apps cost more than one cross-platform build in Flutter or React Native. For most dating apps, cross-platform is enough and saves around 25% to 35%.</li><li>Backend complexity. Real-time chat, location matching and media storage need a backend that handles traffic spikes. Weak architecture is cheap at launch and expensive at 50,000 users.</li></ol><p>Design matters more than people expect. Dating apps live or die on first impressions, and a polished onboarding flow often pays for itself in retention.</p><h3>How to choose a dating app development company in Australia</h3><p>The cheapest quote is rarely the best one. Founders who pick the lowest bid often spend double fixing a chat system that cannot handle concurrent users.</p><p>Use this checklist when comparing teams:</p><ul><li>Have they shipped consumer apps with real-time chat, not just brochure sites?</li><li>Can you speak to a past client?</li><li>How do they handle moderation, reporting and blocking?</li><li>Will you own the source code, design files and cloud accounts from day one?</li><li>How do they price change requests once the prototype exists?</li></ul><p>A team that answers these clearly usually delivers clearly. That is the standard 7 Pillars works to, starting with your niche and budget and recommending the smallest product that can prove demand. If you are considering AI matching, this guide from a <a href="https://7pillars.com.au/blog/how-to-build-a-dating-app-like-tinder-using-ai-features-cost-tech-stack/">dating app development company</a> that publishes its stack and costs openly shows what a transparent estimate looks like.</p><h3>Hidden costs Australian founders miss</h3><p>The build quote is only part of the spend. Plan for these from the start.</p><ul><li>Cloud hosting and storage: AUD 500 to 3,000 a month, depending on user numbers and video use.</li><li>App store fees: Apple charges USD 99 a year and Google charges a one-time USD 25.</li><li>Third-party services: SMS verification, push notifications, email and maps APIs usually bill per use.</li><li>Moderation: automated photo screening plus a human review process.</li><li>Maintenance: 15% to 20% of the build cost each year for updates, OS changes and bug fixes.</li></ul><p>Compliance is the other cost. The Privacy Act 1988 and the Australian Privacy Principles apply to the personal data you collect, and dating apps collect sensitive information such as sexual orientation. The Online Safety Act 2021 also gives the eSafety Commissioner powers over harmful content, so reporting tools and clear takedown processes are worth building early. Get legal advice on your privacy policy before launch, not after.</p><h3>Ways to keep dating app development cost under control</h3><p>You can control the budget without gutting the product. These moves work in practice.</p><p>Start with a niche. An app for a specific community, city or interest needs fewer users to feel alive and makes early marketing cheaper.</p><p>Launch with matching, chat and profiles only. Add video, AI features and premium tiers once you have real usage data. Retention after 90 days will tell you what to build next far better than a guess.</p><p>Use proven services for solved problems. Authentication, payments and push notifications do not need to be built from scratch.</p><p>Set a fixed budget per phase and agree the scope in writing. A working release every four to six weeks lets you stop or redirect before money is wasted.</p><p>Plan monetisation early. Subscriptions, boosts and super likes are cheaper to design in than to retrofit.</p><p>This phased, scope-first method is how <a href="https://7pillars.com.au/">7 Pillars</a> runs dating app projects, and it is the reason founders leave a first call with a feature list and a realistic estimate rather than a vague quote.</p><h3>FAQs</h3><h4>How much does it cost to build a dating app in Australia?</h4><p>Most Australian projects cost between AUD 15,000 and 300,000. A basic MVP sits at the low end, while apps with AI matching, video and heavy moderation sit at the high end.</p><h4>How long does it take to build a dating app?</h4><p>An MVP takes three to five months. A mid-level app takes five to eight months, and an advanced app can take up to a year.</p><h4>Is it cheaper to hire an offshore team?</h4><p>Usually, yes. Hourly rates can be half or less. The risk is communication gaps and time zones, so many founders keep product and design local and outsource engineering.</p><h4>What ongoing costs should I expect after launch?</h4><p>Hosting, third-party services, moderation, app store fees and maintenance. A safe planning figure is 15% to 25% of the initial build cost each year.</p><h4>How can I get an accurate estimate for my dating app idea?</h4><p>Share your niche, must-have features and budget with a development team and ask for a phased estimate. 7 Pillars offers scoping calls where you get a feature list and phase-wise costing before you commit.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b55bbd189f60" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/dating-app-development-cost-in-australia-2026-guide-b55bbd189f60">Dating App Development Cost in Australia: 2026 Guide</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[You Know JavaScript. Your Brain Just Forgets. (Part 1: How JavaScript Thinks)]]></title>
            <link>https://javascript.plainenglish.io/you-know-javascript-your-brain-just-forgets-part-1-how-javascript-thinks-73bd7055cfe4?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/73bd7055cfe4</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[javascript-development]]></category>
            <category><![CDATA[javascript-interview]]></category>
            <category><![CDATA[javascript-tips]]></category>
            <dc:creator><![CDATA[Sonia Goplani]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 15:06:03 GMT</pubDate>
            <atom:updated>2026-09-30T15:06:01.782Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aLdh21F_r_uCM95TESPZyw.png" /></figure><h3>Before We Begin</h3><p>You’ve shipped features. Fixed bugs at 2 a.m. Reviewed a mountain of pull requests.</p><p>Then the interviewer smiles and asks, “So, what’s the temporal dead zone?”</p><p>And your brain returns… undefined.</p><p>Or it’s closures. Or Promise.race. Or starvation. Or inversion of control.</p><p><em>Quick check: if you just answered all of those in your head, congratulations. Close this tab and go get a coffee, you’ve earned it. Still here? Welcome. You’re in very good company.</em></p><p>This isn’t a knowledge problem. It’s a recall problem. We use these things every day. We just stop calling them by their names.</p><p>So here’s your cheat sheet, in two parts. Part 1 is how JavaScript thinks: 11 fundamentals, each with a one-line answer, a tiny example and a picture.</p><p><strong>Five minutes before the interview?</strong> Just read the one-liners at the top of each section and the <strong>Remember it</strong> lines.</p><p>Let’s go.</p><h3>1. JavaScript and the Execution Context: Where It All Begins</h3><blockquote><strong><em>In one line:</em></strong><em> JavaScript is a synchronous, single-threaded language, and all code runs inside an execution context.</em></blockquote><p>Picture JavaScript as a chef with one pair of hands. One dish at a time, in order. No multitasking, no skipping ahead.</p><h3>The kitchen: execution context</h3><p>Every program gets one big kitchen, the <strong>Global Execution Context</strong>. It’s set up in two phases:</p><ol><li><strong>Memory phase (gather the ingredients):</strong> variables and functions get memory as key-value pairs. Variables start as undefined.</li><li><strong>Code phase (start cooking):</strong> code runs one line at a time.</li></ol><p>Call a function, and a new mini kitchen opens inside the big one. When the function returns, it closes.</p><pre>var n = 10;</pre><pre>function square(num) {<br>  var ans = num * num;<br>  return ans;<br>}</pre><pre>square(n);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C-PbisFTzNhmRuIroG-Oyg.png" /></figure><p>Memory side = <strong>Variable Environment</strong>. Code side = <strong>Thread of Execution</strong>. (The diagram shows n: 10, which is memory after the code has run.)</p><p><strong>Remember it:</strong> Two phases, memory then code. Every function call gets its own execution context, which disappears when it returns.</p><h3>2. Hoisting: Using Things Before You Write Them</h3><blockquote><strong><em>In one line:</em></strong><em> variables and functions get memory before the code runs, so you can use them before they’re declared.</em></blockquote><p>Think of a restaurant that reserves your table before you arrive. The table is ready, but the chair is empty. That empty chair is undefined.</p><p>Functions are the VIP guests. They arrive fully seated, so calling them early works.</p><pre>console.log(n);   // undefined<br>greet();          // &quot;Hello!&quot;</pre><pre>var n = 10;</pre><pre>function greet() {<br>  console.log(&quot;Hello!&quot;);<br>}</pre><p><strong>Remember it:</strong> Hoisting = memory first, code later. var gives undefined early, functions work fully.</p><h3>3. let, const and the Temporal Dead Zone: The Waiting Room</h3><blockquote><strong><em>In one line:</em></strong><em> </em><em>let and </em><em>const are hoisted too, but you can&#39;t touch them until their line runs.</em></blockquote><p>var is the relaxed guest: &quot;I&#39;m here, I&#39;m just empty.&quot;</p><p>let and const sit in a waiting room from the moment they&#39;re hoisted until their line runs. That waiting room is the <strong>temporal dead zone</strong>. Call them out early, and JavaScript throws an error.</p><pre>console.log(a); // undefined<br>console.log(b); // ReferenceError: Cannot access &#39;b&#39; before initialization</pre><pre>var a = 10;<br>let b = 20;</pre><p><strong>Interview trap: two errors that look alike</strong></p><ul><li>Cannot access &#39;b&#39; before initialization: it exists, it&#39;s just still in the waiting room.</li><li>b is not defined: it was never declared at all.</li></ul><p><strong>Remember it:</strong> The temporal dead zone is the time between hoisting and initialization of let and const. Access them there, and you get a ReferenceError.</p><h3>4. Scope and the Scope Chain: Who Can See What</h3><blockquote><strong><em>In one line:</em></strong><em> scope is where a variable can be used, and it’s decided by where the code is written.</em></blockquote><p>Think of rooms inside rooms. A child room can peek into its parent’s room, all the way out to the front door. But a parent can never peek into the child’s room.</p><p>“Lexical” sounds fancy, but it only means <strong>where the code physically sits</strong>.</p><ul><li><strong>Lexical environment</strong> = local memory + a reference to the parent’s lexical environment.</li><li><strong>Scope chain</strong> = the path JavaScript follows to find a variable: own room, then parent, then all the way to global.</li></ul><pre>function a() {<br>  var x = 7;</pre><pre>  function c() {<br>    console.log(x); // 7, found in the parent&#39;s room<br>  }</pre><pre>  c();<br>}</pre><pre>a();</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OCpmXGxbm76DyPxLqyzZ7g.png" /></figure><p><strong>Fun fact:</strong> even an empty JavaScript file gets a global execution context. In the browser, you also get a free window object and a this that points to it.</p><p><strong>Remember it:</strong> Lexical environment = local memory + parent’s reference. Following those references upward is the scope chain.</p><h3>5. Block Scope and Shadowing: What Happens Inside { }</h3><blockquote><strong><em>In one line:</em></strong><em> </em><em>let and </em><em>const stay inside the curly braces. </em><em>var escapes.</em></blockquote><p>A <strong>block</strong> is code wrapped in { }, also called a <strong>compound statement</strong>. It lets you put many statements where JavaScript expects just one, like after an if.</p><p>Inside a block, let and const get their own private memory space. var doesn&#39;t care about walls. It walks right out.</p><pre>{<br>  var a = 10;<br>  let b = 20;<br>}</pre><pre>console.log(a); // 10, var escaped<br>console.log(b); // ReferenceError, let stayed inside</pre><h3>Shadowing: the inner one wins</h3><p>Same name inside and outside a block? Inside the block, the inner variable “shadows” the outer one. Outside, the outer one is untouched.</p><pre>let b = 100;</pre><pre>{<br>  let b = 20;<br>  console.log(b); // 20<br>}</pre><pre>console.log(b); // 100</pre><h3>Illegal shadowing: when var breaks the rules</h3><pre>let a = 10;</pre><pre>{<br>  var a = 20; // SyntaxError: Identifier &#39;a&#39; has already been declared<br>}</pre><p>var tries to escape the block and crashes into the let that already lives outside. (The reverse, var outside and let inside, is perfectly legal.)</p><p><strong>Remember it:</strong> Block = code in { }. Shadowing = the inner variable hides the outer one. Shadowing a let with a var is illegal.</p><h3>6. Closures: The Function With a Backpack</h3><blockquote><strong><em>In one line:</em></strong><em> a closure is a function bundled together with its lexical environment.</em></blockquote><p>When a function leaves home, it packs a backpack with every variable it could see. Even after its parent function is long gone, the backpack stays on.</p><pre>function x() {<br>  var a = 7;<br>  function y() {<br>    console.log(a);<br>  }<br>  a = 100;<br>  return y;<br>}</pre><pre>var z = x();   // x is done and gone<br>z();           // 100</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JOUD5XnTPtgzIj_20hPQyA.png" /></figure><p><strong>Plot twist:</strong> it prints 100, not 7. The backpack holds <strong>references</strong>, not photocopies. Change the value, and the closure sees the change.</p><h3>The famous setTimeout loop</h3><pre>for (var i = 1; i &lt;= 3; i++) {<br>  setTimeout(() =&gt; console.log(i), i * 1000);<br>}<br>// 4, 4, 4</pre><pre>for (let i = 1; i &lt;= 3; i++) {<br>  setTimeout(() =&gt; console.log(i), i * 1000);<br>}<br>// 1, 2, 3</pre><p>With var, all three callbacks share one i. With let, every loop round gets its own fresh i.</p><h3>Data hiding: a secret only the closure knows</h3><pre>function createCounter() {<br>  let count = 0;<br>  return () =&gt; ++count;<br>}</pre><pre>const counter = createCounter();<br>counter(); // 1<br>counter(); // 2<br>// nobody outside can touch count</pre><p><strong>Where you’ll meet closures:</strong> the module pattern, currying, once, memoize, async state, setTimeout and iterators.</p><p><strong>One caution:</strong> a backpack you never take off gets heavy. Closures keep variables alive, so careless use can hold memory longer than needed.</p><p><strong>Remember it:</strong> A closure remembers the variables from where it was written, even after the outer function has returned. It holds references, not copies.</p><h3>7. Currying: One Argument at a Time</h3><blockquote><strong><em>In one line:</em></strong><em> currying turns </em><em>f(a, b) into </em><em>f(a)(b), and it works because of closures.</em></blockquote><p>Ordering coffee: “Size?” Large. “Milk?” Oat. “Sugar?” None. One question at a time, and the barista remembers every answer. That memory is a closure.</p><pre>const add = a =&gt; b =&gt; a + b;</pre><pre>add(2)(3);             // 5</pre><pre>const addTwo = add(2); // a = 2 is remembered<br>addTwo(10);            // 12</pre><p><strong>Where it helps:</strong> reusing a function with some arguments already fixed, like addTwo above.</p><p><strong>Remember it:</strong> Currying = one argument at a time, powered by closures.</p><h3>8. The this Keyword: It Depends Who’s Asking</h3><blockquote><strong><em>In one line:</em></strong><em> </em><em>this is decided by </em><strong><em>how</em></strong><em> a function is called, not where it is written.</em></blockquote><p>this is like the word &quot;here&quot;. Where &quot;here&quot; points depends on where you&#39;re standing when you say it.</p><p><strong>Where does </strong><strong>this point?</strong></p><ul><li><strong>Global scope:</strong> the global object (window in the browser).</li><li><strong>Inside a regular function call:</strong> undefined in strict mode, window otherwise.</li><li><strong>Inside a method, </strong><strong>obj.fn():</strong> the object before the dot.</li><li><strong>Inside an arrow function:</strong> it has no this of its own, so it borrows one from the surrounding code.</li><li><strong>With </strong><strong>call, </strong><strong>apply or </strong><strong>bind:</strong> whatever you choose.</li><li><strong>Inside an event listener (regular function):</strong> the element that received the event.</li></ul><pre>const user = {<br>  greet() {<br>    console.log(this === user);<br>  }<br>};</pre><pre>user.greet();            // true, called with user before the dot</pre><pre>const greet = user.greet;<br>greet();                 // false, the object got left behind</pre><p><strong>Interview trap:</strong> copy a method into a variable, and it forgets its object. this is not stored with the function, it&#39;s decided at every call.</p><p><strong>Remember it:</strong> this is decided at call time. Quick trick: look left of the dot.</p><h3>9. Arrow Functions: Short, Sweet, and Borrowing Their this</h3><blockquote><strong><em>In one line:</em></strong><em> arrow functions are a shorter way to write functions, and they don’t have their own </em><em>this.</em></blockquote><pre>// Regular function<br>function square(n) {<br>  return n * n;<br>}</pre><pre>// Arrow function<br>const squareArrow = n =&gt; n * n;</pre><p>An arrow function is like a guest who never brings their own umbrella. When it rains, they share yours. Their this is whatever this was around them when they were written.</p><h3>Where that actually helps</h3><pre>const timer = {<br>  seconds: 0,<br>  start() {<br>    setTimeout(function () {<br>      console.log(this.seconds); // undefined, this is not timer<br>    }, 1000);</pre><pre>    setTimeout(() =&gt; {<br>      console.log(this.seconds); // 0, borrowed from start()<br>    }, 1000);<br>  }<br>};</pre><pre>timer.start();</pre><h3>How arrows are different</h3><ul><li><strong>No own </strong><strong>this.</strong> It comes from the surrounding code. This is called <strong>lexical </strong><strong>this</strong>.</li><li><strong>call, </strong><strong>apply and </strong><strong>bind can&#39;t change it.</strong></li><li><strong>No </strong><strong>arguments object.</strong> Use rest parameters, (...args), instead.</li><li><strong>Can’t be used with </strong><strong>new.</strong> They are not constructors.</li><li><strong>Not hoisted like function declarations.</strong> They live in a variable, so a const arrow sits in the temporal dead zone until its line runs.</li></ul><p><strong>Interview trap:</strong> don’t write object methods as arrow functions. They won’t see the object as this.</p><p><strong>Remember it:</strong> Arrow functions are shorter, and they borrow this from where they are written.</p><h3>10. call, apply and bind: Choosing Your this</h3><blockquote><strong><em>In one line:</em></strong><em> </em><em>call and </em><em>apply run a function right now with the </em><em>this you choose. </em><em>bind gives you a new function with </em><em>this locked in.</em></blockquote><p>Now that this makes sense, here are the three ways to take control of it.</p><pre>const person = { name: &quot;Sonia&quot; };</pre><pre>function greet(greeting, mark) {<br>  console.log(greeting + &quot;, &quot; + this.name + mark);<br>}</pre><pre>greet.call(person, &quot;Hello&quot;, &quot;!&quot;);    // Hello, Sonia!<br>greet.apply(person, [&quot;Hello&quot;, &quot;!&quot;]); // Hello, Sonia!</pre><pre>const later = greet.bind(person);<br>later(&quot;Hi&quot;, &quot;!&quot;);                    // Hi, Sonia!</pre><p><strong>The ABC trick:</strong></p><ul><li><strong>C</strong>all = <strong>C</strong>omma-separated arguments.</li><li><strong>A</strong>pply = <strong>A</strong>rray of arguments.</li><li><strong>B</strong>ind = <strong>B</strong>rings back a new function for later.</li></ul><p><strong>Bonus:</strong> give bind some arguments up front, and you get <strong>partial application</strong>, currying&#39;s close cousin.</p><pre>const multiply = (a, b) =&gt; a * b;<br>const double = multiply.bind(null, 2);<br>double(5); // 10</pre><p><strong>Remember it:</strong> call and apply run now, bind returns a function for later. None of them can change an arrow function&#39;s this.</p><h3>11. Prototypes: JavaScript’s Family Tree</h3><blockquote><strong><em>In one line:</em></strong><em> every object has a hidden link to another object, its </em><strong><em>prototype</em></strong><em>, and JavaScript follows that chain to find properties the object doesn’t have itself.</em></blockquote><p>Your wallet is empty, so you ask your parent. Their wallet is empty too, so they ask your grandparent. That’s the <strong>prototype chain</strong>.</p><p>Ever wondered where push comes from? You never wrote it.</p><pre>const arr = [1, 2, 3];<br>arr.push(4); // push lives on Array.prototype, not on arr</pre><p>The chain: arr → Array.prototype → Object.prototype → null. When the chain reaches null, JavaScript gives up and returns undefined.</p><h3>Building your own family tree</h3><pre>const animal = {<br>  eat() { console.log(&quot;Eating&quot;); }<br>};</pre><pre>const dog = Object.create(animal); // animal is dog&#39;s prototype<br>dog.bark = () =&gt; console.log(&quot;Woof&quot;);</pre><pre>dog.bark(); // found on dog itself<br>dog.eat();  // not on dog, found on its prototype</pre><p>This is <strong>prototypal inheritance</strong>: objects inheriting directly from other objects.</p><p><strong>Sound familiar?</strong> It works just like the scope chain. The scope chain finds variables, and the prototype chain finds properties.</p><h3>The two names that confuse everyone</h3><ul><li><strong>__proto__</strong> is the link every object has to its prototype. (The modern way to read it is Object.getPrototypeOf(obj).)</li><li><strong>prototype</strong> is a property on functions and classes. Objects created with new get linked to it.</li></ul><pre>class Person {<br>  greet() { console.log(&quot;Hi&quot;); }<br>}</pre><pre>const sonia = new Person();<br>Object.getPrototypeOf(sonia) === Person.prototype; // true</pre><p><strong>Interview trap:</strong> classes in JavaScript are syntactic sugar. Under the hood, it’s prototypes all the way down.</p><p><strong>Remember it:</strong> Every object links to a prototype. JavaScript walks that chain to find properties, ending at null.</p><h3>Two-Minute Revision: Read This in the Elevator</h3><p>All of Part 1 in 16 lines. Screenshot it, save it, read it on the way in.</p><ul><li><strong>JavaScript:</strong> a synchronous, single-threaded language.</li><li><strong>Execution context:</strong> built in two phases, memory creation and code execution.</li><li><strong>Function call:</strong> creates a new execution context, deleted when the function returns.</li><li><strong>Hoisting:</strong> memory is given before code runs, so var gives undefined early and functions work fully.</li><li><strong>Temporal dead zone:</strong> the time between hoisting and initialization of let and const.</li><li><strong>Lexical environment:</strong> local memory plus a reference to the parent’s lexical environment.</li><li><strong>Scope chain:</strong> the chain of lexical environments JavaScript follows to find a variable.</li><li><strong>Block scope:</strong> let and const stay inside { }, var leaks out.</li><li><strong>Shadowing:</strong> an inner variable hides an outer one. Shadowing a let with a var is illegal.</li><li><strong>Closure:</strong> a function bundled with its lexical environment. It remembers references, not copies.</li><li><strong>Currying:</strong> f(a, b) becomes f(a)(b), powered by closures.</li><li><strong>this:</strong> decided at call time. Look left of the dot.</li><li><strong>Arrow functions:</strong> shorter, with no own this. They borrow it from where they&#39;re written.</li><li><strong>call, apply, bind:</strong> comma-separated, array, and a new function for later.</li><li><strong>Prototype chain:</strong> objects link to prototypes, and JavaScript walks the chain to find properties, ending at null.</li><li><strong>Classes:</strong> syntactic sugar over prototypes.</li></ul><h3>One Last Thing</h3><p>If a word slips away mid-interview, don’t panic. Your brain didn’t delete it. It’s just in the temporal dead zone for a moment.</p><p>Take a breath. Picture the boxes, the backpack and the VIP line. Start with the one-liner, and the rest will follow.</p><p><strong>Coming up in Part 2, How JavaScript Waits and Reacts:</strong> callbacks, event bubbling, promises, the event loop, async/await, and a peek under the engine’s hood.</p><p>Save this. Share it with someone who has an interview this week. Come back whenever your memory returns undefined.</p><p>You’ve got this.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=73bd7055cfe4" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/you-know-javascript-your-brain-just-forgets-part-1-how-javascript-thinks-73bd7055cfe4">You Know JavaScript. Your Brain Just Forgets. (Part 1: How JavaScript Thinks)</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Top 5 tools that stops your Coding Agent from rambling.]]></title>
            <link>https://javascript.plainenglish.io/top-5-tools-that-stops-your-coding-agent-from-rambling-359aba9a7093?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/359aba9a7093</guid>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[ai-coding-agent]]></category>
            <category><![CDATA[agent-harness]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Krish]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 15:05:37 GMT</pubDate>
            <atom:updated>2026-09-30T15:05:36.308Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*palpxD5l-KTsqKrgzfOn6A.png" /></figure><p>I’ve been experimenting with different ways to make my coding agent waste fewer tokens and stay focused on the task. Here’s what I’ve found.</p><p>Your coding agent wastes more tokens in two directions at once.</p><ol><li>It reads too much: dumping the entire output of npm install or a 500-test pytest run straight into its context.</li><li>Then it writes too much, burying the one line you needed under three paragraphs you didn&#39;t ask for.</li></ol><p>Both halves are fixable. A surprising number of people have built tools for it, and here are five that each attack a different point in the loop.</p><h3>1. i-have-adhd</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rENDnWOfdtgps52z7MCSbQ.png" /><figcaption>i-have-adhd (github)</figcaption></figure><p>The name is blunt and so is the tool. It’s a skill that stops your agent burying the answer.</p><p>Basically it applies ten rules to how the agent responds: action first, numbered steps, concrete time estimates, no preamble. Ask it something and the answer arrives at the top instead of after a warm-up paragraph.</p><p>It only changes what the agent writes back to you. It doesn’t touch what the agent reads. But if your main complaint is scrolling past filler to find the command you asked for, it’s the smallest possible fix.</p><p>At 48.7k stars it’s one of the most-starred agent skills on GitHub, and the repo is active.</p><p>Try here: github.com/ayghri/i-have-adhd</p><h3>2. RTK</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2G0fZnJ14LNDMIIsRtraBg.png" /><figcaption>RTK(github)</figcaption></figure><p>RTK works the other side of the problem. It shrinks shell output before your agent ever sees it.</p><p>It’s a CLI proxy — a single Rust binary, no dependencies, sitting between the terminal and the agent, applying truncation, grouping and deduplication to whatever comes back.</p><p>It covers over 100 commands: ls, cat, grep, git, pytest, jest, cargo, Docker, kubectl, pnpm, pip, the AWS CLI, Pulumi.</p><p>The project reports 60–90% reduction in bash output. The repo is unusually honest about what that means . it says it measures output reduction, not bill reduction, and that its token estimates are bytes divided by four, so “percentages are reliable but absolute token numbers are approximate.”</p><p>Install is one line: brew install rtk. 81k stars, 2,000+ commits, five named core contributors.</p><p>Try here: <a href="https://github.com/rtk-ai/rtk">github.com/rtk-ai/rtk</a></p><h3>3. context-mode</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uUp2I5SXuFineB9QA1VPoA.png" /><figcaption>context-mode (github)</figcaption></figure><p>context-mode goes after the thing that actually kills long sessions: tool results flooding the active context.</p><p>It runs as an MCP server and sandboxes tool output so raw data never enters the context window. It also tracks the session file edits, git operations, errors, decisions in a local SQLite database. When the conversation compacts, it searches that history and pulls back only what’s relevant instead of losing everything.</p><p>Its own benchmarks put a Playwright snapshot at 56 KB down to 299 bytes, and a full session at 315 KB down to 5.4 KB. Those are the project’s numbers, not independently measured ones.</p><p>And it works with Claude Code, Cursor, Gemini CLI and VS Code Copilot. 23.7k stars, MIT/ELv2.</p><p>One thing to know before you go looking: there are a lot of forks with identical descriptions floating around. The original is mksglu/context-mode.</p><p>Try here: <a href="https://github.com/mksglu/context-mode">github.com/mksglu/context-mode</a></p><h3>4. Caveman</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6O1LVOpCvK6IDQAPUTkAqg.png" /><figcaption>Caveman (github)</figcaption></figure><p>This was a popular tool initially. Caveman does one thing, with a very specific sense of humour. It makes your agent talk like a caveman.</p><p>It provides Fewer articles, filler words and hedges. The project reports a 65% average cut in output tokens. It ships as both a skill and a proxy, and exposes MCP tools for compressing and retrieving content.</p><p>The more interesting part is caveman learn. It reads your local agent history, ranks your worst token sinks from the top down with a one-line fix behind each, and can apply those fixes, automatically reverting any change that doesn&#39;t lower tokens per turn.</p><p>Like i-have-adhd, it only affects output, not input. It doesn’t rewrite your prompts.</p><p>Try here: <a href="https://github.com/juliusbrussee/caveman">github.com/juliusbrussee/caveman</a></p><h3>5. Headroom</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_aKoJLeSrgkFVmeQfl5o7A.png" /><figcaption>Headroom (github)</figcaption></figure><p>Headroom is the broadest of the five. It sits between your agent and the model provider and compresses everything passing through. The tool outputs, logs, files and RAG chunks.</p><p>You launch your agent through it and every read gets compressed on the way to the model. It works with Claude Code, Codex, Cursor, Aider, Copilot and OpenClaw, and runs as a wrapper, proxy, SDK call or MCP server depending on how you want to wire it in.</p><p>The project reports roughly 20% fewer tokens for coding agents and 60–95% for JSON, with compression that’s reversible.</p><p>If the others are point fixes, this is the one covering the whole inbound stream.</p><p>Try here: <a href="https://github.com/headroomlabs-ai/headroom">github.com/headroomlabs-ai/headroom</a></p><h3>Read this before you try these tools:</h3><p>One thing i would like to address here is the Compression mechanism has also a failure mode, and it’s worth knowing before you install three of these at once.</p><p>Trim a tool response too aggressively and the model may notice something is missing and go get it, reopening the original output, or just rerunning the command. Now you’ve paid for the compressed version, the original, and an extra turn. The task takes longer and costs more than if you’d left it alone.</p><p>This comes from developers reporting it rather than a controlled benchmark, so treat it as a caution rather than a measurement. It does suggest starting with one tool, not five.</p><h3>Takeaway</h3><p>These split cleanly into two groups. <strong>i-have-adhd</strong> and <strong>Caveman</strong> change what your agent writes. <strong>RTK</strong>,<strong> context-mode</strong> and <strong>Headroom</strong> change what it reads.</p><p>If you’ve never touched any of this, start on the reading side, that’s where the volume is. RTK is the easiest first install: one Homebrew command, covering the commands you already run all day.</p><p>Every number above comes from the projects themselves. None has been independently benchmarked, so treat the percentages as claims rather than guarantees, and check your own usage before and after.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=359aba9a7093" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/top-5-tools-that-stops-your-coding-agent-from-rambling-359aba9a7093">Top 5 tools that stops your Coding Agent from rambling.</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Top 5 JavaScript Projects You Should Build in 2026 (If You Want to Get Hired)]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://javascript.plainenglish.io/top-5-javascript-projects-you-should-build-in-2026-if-you-want-to-get-hired-358682a7dc41?source=rss----4b3a1ed4f11c---4"><img src="https://cdn-images-1.medium.com/max/600/1*D0-XGtoYJoYCL89H5JOkBA.jpeg" width="600"></a></p><p class="medium-feed-snippet">Last year, I reviewed my own portfolio and felt sick. A to-do app. A weather app. A calculator. The same 3 projects every tutorial on&#x2026;</p><p class="medium-feed-link"><a href="https://javascript.plainenglish.io/top-5-javascript-projects-you-should-build-in-2026-if-you-want-to-get-hired-358682a7dc41?source=rss----4b3a1ed4f11c---4">Continue reading on JavaScript in Plain English »</a></p></div>]]></description>
            <link>https://javascript.plainenglish.io/top-5-javascript-projects-you-should-build-in-2026-if-you-want-to-get-hired-358682a7dc41?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/358682a7dc41</guid>
            <category><![CDATA[saas]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[projects]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Shubh]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 12:07:56 GMT</pubDate>
            <atom:updated>2026-09-30T12:07:55.160Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[AI Agents + Playwright: The Download Worked. The Report Was Wrong.]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://javascript.plainenglish.io/ai-agents-playwright-the-download-worked-the-report-was-wrong-02affa12f71b?source=rss----4b3a1ed4f11c---4"><img src="https://cdn-images-1.medium.com/max/2600/0*_gLoPLvEq403Hr3J" width="6048"></a></p><p class="medium-feed-snippet">A production-style workflow that turns agent exploration into reviewed browser tests &#x2014; and checks the file, not just the button.</p><p class="medium-feed-link"><a href="https://javascript.plainenglish.io/ai-agents-playwright-the-download-worked-the-report-was-wrong-02affa12f71b?source=rss----4b3a1ed4f11c---4">Continue reading on JavaScript in Plain English »</a></p></div>]]></description>
            <link>https://javascript.plainenglish.io/ai-agents-playwright-the-download-worked-the-report-was-wrong-02affa12f71b?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/02affa12f71b</guid>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[test-automation]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[playwrights]]></category>
            <category><![CDATA[technology]]></category>
            <dc:creator><![CDATA[Sonia Akther]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 10:41:25 GMT</pubDate>
            <atom:updated>2026-09-30T10:41:23.864Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Why Does Instagram Process Your Photo After You Upload It?]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://javascript.plainenglish.io/why-does-instagram-process-your-photo-after-you-upload-it-dd19d6310be7?source=rss----4b3a1ed4f11c---4"><img src="https://cdn-images-1.medium.com/max/1976/1*5lzwTwlsdS26yLXt4VIB6A.png" width="1976"></a></p><p class="medium-feed-snippet">What message queues teach us about background processing, system reliability, and handling millions of requests.</p><p class="medium-feed-link"><a href="https://javascript.plainenglish.io/why-does-instagram-process-your-photo-after-you-upload-it-dd19d6310be7?source=rss----4b3a1ed4f11c---4">Continue reading on JavaScript in Plain English »</a></p></div>]]></description>
            <link>https://javascript.plainenglish.io/why-does-instagram-process-your-photo-after-you-upload-it-dd19d6310be7?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/dd19d6310be7</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[queue]]></category>
            <category><![CDATA[message-queue]]></category>
            <category><![CDATA[system-design-concepts]]></category>
            <dc:creator><![CDATA[Neha Gupta]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 09:25:25 GMT</pubDate>
            <atom:updated>2026-09-30T09:25:24.486Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Canvas 2D API, Part 1: This 20-year-old API still builds the coolest things on the web]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://javascript.plainenglish.io/canvas-2d-api-tutorial-particle-text-effect-javascript-e8ecfd2f896b?source=rss----4b3a1ed4f11c---4"><img src="https://cdn-images-1.medium.com/max/1092/1*svmLEnAMxtgmHWefYhdmLw.gif" width="1092"></a></p><p class="medium-feed-snippet">How I turned a boring fillText() call into something I couldn&apos;t stop dragging my cursor through using Canvas 2D API.</p><p class="medium-feed-link"><a href="https://javascript.plainenglish.io/canvas-2d-api-tutorial-particle-text-effect-javascript-e8ecfd2f896b?source=rss----4b3a1ed4f11c---4">Continue reading on JavaScript in Plain English »</a></p></div>]]></description>
            <link>https://javascript.plainenglish.io/canvas-2d-api-tutorial-particle-text-effect-javascript-e8ecfd2f896b?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/e8ecfd2f896b</guid>
            <category><![CDATA[canvas]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Usman Writes]]></dc:creator>
            <pubDate>Wed, 30 Sep 2026 08:00:19 GMT</pubDate>
            <atom:updated>2026-09-30T11:53:01.259Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[React Hooks in the Real World: Master the Built-ins and Build Custom Ones That Scale]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://javascript.plainenglish.io/react-hooks-in-the-real-world-master-the-built-ins-and-build-custom-ones-that-scale-16152792f16f?source=rss----4b3a1ed4f11c---4"><img src="https://cdn-images-1.medium.com/max/1932/1*YWR_nxCMbU3g3HeeflXm8Q.jpeg" width="1932"></a></p><p class="medium-feed-snippet">Go beyond the basics &#x2014; write scalable, readable, and reusable components with custom React Hooks</p><p class="medium-feed-link"><a href="https://javascript.plainenglish.io/react-hooks-in-the-real-world-master-the-built-ins-and-build-custom-ones-that-scale-16152792f16f?source=rss----4b3a1ed4f11c---4">Continue reading on JavaScript in Plain English »</a></p></div>]]></description>
            <link>https://javascript.plainenglish.io/react-hooks-in-the-real-world-master-the-built-ins-and-build-custom-ones-that-scale-16152792f16f?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/16152792f16f</guid>
            <category><![CDATA[react-hook]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[frontend-development]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Rakesh Kumar]]></dc:creator>
            <pubDate>Tue, 29 Sep 2026 14:03:18 GMT</pubDate>
            <atom:updated>2026-09-29T14:03:16.823Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[CDN Explained: The System Design Concept Every Developer Should Understand]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://javascript.plainenglish.io/cdn-explained-the-system-design-concept-every-developer-should-understand-b7cc86c1c541?source=rss----4b3a1ed4f11c---4"><img src="https://cdn-images-1.medium.com/max/1964/1*difddEoqlxUXWpTC0rWHgA.png" width="1964"></a></p><p class="medium-feed-snippet">How edge servers, caching, and intelligent routing make websites faster, reduce backend load, and protect applications.</p><p class="medium-feed-link"><a href="https://javascript.plainenglish.io/cdn-explained-the-system-design-concept-every-developer-should-understand-b7cc86c1c541?source=rss----4b3a1ed4f11c---4">Continue reading on JavaScript in Plain English »</a></p></div>]]></description>
            <link>https://javascript.plainenglish.io/cdn-explained-the-system-design-concept-every-developer-should-understand-b7cc86c1c541?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/b7cc86c1c541</guid>
            <category><![CDATA[cdn-service]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[cdn]]></category>
            <category><![CDATA[system-design-interview]]></category>
            <category><![CDATA[system-design-concepts]]></category>
            <dc:creator><![CDATA[Neha Gupta]]></dc:creator>
            <pubDate>Tue, 29 Sep 2026 12:14:59 GMT</pubDate>
            <atom:updated>2026-09-29T12:14:58.651Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Stop Losing Errors to the Void: Handling Unhandled Promise Rejections Globally in JavaScript…]]></title>
            <link>https://javascript.plainenglish.io/stop-losing-errors-to-the-void-handling-unhandled-promise-rejections-globally-in-javascript-cc321b4e237c?source=rss----4b3a1ed4f11c---4</link>
            <guid isPermaLink="false">https://medium.com/p/cc321b4e237c</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Rajat]]></dc:creator>
            <pubDate>Tue, 29 Sep 2026 11:37:09 GMT</pubDate>
            <atom:updated>2026-09-29T11:37:08.427Z</atom:updated>
            <content:encoded><![CDATA[<h3>Stop Losing Errors to the Void: Handling Unhandled Promise Rejections Globally in JavaScript, Angular, and React</h3><h4><em>A practical, copy-paste guide to catching the promise rejections your app is currently swallowing without you knowing it</em></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xdjjp3i6152V4hFdqWOBWA.png" /></figure><p>Have you ever had a user tell you “the button just didn’t do anything” — and when you checked the console, there was a red error sitting there that nobody ever saw? That’s usually an unhandled promise rejection, quietly failing in the background while your app pretends everything is fine.</p><p>By the end of this article, you’ll know how to:</p><ul><li>Catch every unhandled promise rejection in a browser app, globally, in one place</li><li>Understand exactly what triggers unhandledrejection versus a regular try/catch</li><li>Wire this into an Angular app using the new control flow syntax and signals</li><li>Wire this into a React app using a clean custom hook</li><li>Write unit tests for both so this doesn’t silently break later</li><li>Avoid the handful of mistakes that make this “solution” useless in production</li></ul><p>If you’ve ever shipped a feature, felt good about your error handling, and then found out three weeks later that a whole class of failures was never being logged anywhere — this one’s for you.</p><p>If this kind of “here’s the gap in your error handling” deep-dive is useful, go ahead and hit follow — I write one of these every week, and the next one is about a related blind spot in error boundaries.</p><blockquote><em>Before we dive into the examples, a quick note: the code snippets here are meant purely for understanding the concept. Some syntax may reflect patterns from earlier Angular or React versions by the time you’re reading this. Always check the official docs for the current API and syntax.</em></blockquote><h3>What “Unhandled Promise Rejection” Actually Means</h3><p>A promise rejection is “unhandled” when a promise fails and nothing in the chain has a .catch() or a try/catch around an await to deal with it. JavaScript doesn&#39;t throw immediately — it waits until the current microtask queue drains, and if the rejection still has no handler attached, the runtime fires an unhandledrejection event on the global object.</p><p>That’s the key detail most people miss: it’s not the same mechanism as a thrown synchronous error, and window.onerror will not catch it. These are two separate reporting channels, and a lot of &quot;global error handling&quot; setups only wire up one of them.</p><p>Here’s the plain vanilla JS version, no framework involved:</p><pre>window.addEventListener(&#39;unhandledrejection&#39;, (event) =&gt; {<br>  console.error(&#39;Unhandled promise rejection:&#39;, event.reason);<br><br>// Optional: stop the browser from also logging its own default message<br>  event.preventDefault();<br>  // This is where you&#39;d send it to Sentry, LogRocket, your own logging endpoint, etc.<br>});</pre><p>You can also assign it as a property instead of an event listener:</p><pre>window.onunhandledrejection = (event) =&gt; {<br>  console.error(&#39;Unhandled promise rejection:&#39;, event.reason);<br>};</pre><p>The event-listener version is generally the better choice, because onunhandledrejection gets overwritten if two parts of your codebase both try to set it. addEventListener lets multiple listeners coexist.</p><p>There’s also a companion event worth knowing: rejectionhandled. It fires if a promise was initially unhandled but <em>later</em> got a .catch() attached (common with async timing quirks). If you&#39;re building serious error tracking, you&#39;ll want to listen for both so you don&#39;t log false positives.</p><h3>Wiring This Into an Angular App</h3><p>In Angular, zone.js patches Promise under the hood, which means unhandled rejections inside the zone still bubble up to the same window unhandledrejection event — so the approach above works without modification. The cleanest way to use it is a small injectable service that exposes the last error as a signal, so any component can react to it.</p><pre>// error-tracking.service.ts<br>import { Injectable, signal } from &#39;@angular/core&#39;;<br><br>@Injectable({ providedIn: &#39;root&#39; })<br>export class ErrorTrackingService {<br>  readonly lastError = signal&lt;string | null&gt;(null);<br>  constructor() {<br>    window.addEventListener(&#39;unhandledrejection&#39;, this.handleRejection);<br>  }<br>  private handleRejection = (event: PromiseRejectionEvent) =&gt; {<br>    const message = event.reason?.message ?? String(event.reason);<br>    console.error(&#39;Unhandled promise rejection:&#39;, event.reason);<br>    this.lastError.set(message);<br>    event.preventDefault();<br>  };<br>}</pre><p>And a small component that surfaces it, using the new @if control flow and an input() signal for styling flexibility:</p><pre>// error-banner.component.ts<br>import { Component, inject, input } from &#39;@angular/core&#39;;<br>import { ErrorTrackingService } from &#39;./error-tracking.service&#39;;<br><br>@Component({<br>  selector: &#39;app-error-banner&#39;,<br>  template: `<br>    @if (errorTracking.lastError(); as error) {<br>      &lt;div [class]=&quot;bannerClass()&quot;&gt;<br>        {{ error }}<br>      &lt;/div&gt;<br>    }<br>  `,<br>})<br>export class ErrorBannerComponent {<br>  protected errorTracking = inject(ErrorTrackingService);<br>  bannerClass = input(&#39;error-banner&#39;);<br>}</pre><p>Because ErrorTrackingService is providedIn: &#39;root&#39;, its constructor runs once, the first time it&#39;s injected anywhere — which in practice means as soon as your app bootstraps and something requests it. Drop &lt;app-error-banner /&gt; near the root of your app and you have a working global catch-all.</p><h3>Unit Testing the Angular Version</h3><p>The tricky part of testing this is that PromiseRejectionEvent isn&#39;t something you can construct with a plain object — you have to build a regular Event and attach a reason property to it, then dispatch it on window:</p><pre>// error-tracking.service.spec.ts<br>import { TestBed } from &#39;@angular/core/testing&#39;;<br>import { ErrorTrackingService } from &#39;./error-tracking.service&#39;;<br><br>describe(&#39;ErrorTrackingService&#39;, () =&gt; {<br>  let service: ErrorTrackingService;<br>  beforeEach(() =&gt; {<br>    TestBed.configureTestingModule({});<br>    service = TestBed.inject(ErrorTrackingService);<br>  });<br>  it(&#39;captures an unhandled promise rejection from window&#39;, () =&gt; {<br>    const rejectionEvent = new Event(&#39;unhandledrejection&#39;) as PromiseRejectionEvent;<br>    Object.defineProperty(rejectionEvent, &#39;reason&#39;, {<br>      value: new Error(&#39;Something broke&#39;),<br>    });<br>    window.dispatchEvent(rejectionEvent);<br>    expect(service.lastError()).toBe(&#39;Something broke&#39;);<br>  });<br>});</pre><p>Quick, answerable one for you: have you ever shipped a “silent” promise rejection to production and only found out about it from a confused user report instead of your monitoring tool? Drop the story in the comments — I want to hear how you eventually tracked it down.</p><h3>Wiring This Into a React App</h3><p>React doesn’t patch promises, so the pattern is simpler: a custom hook that attaches the listener on mount and cleans it up on unmount.</p><pre>// useUnhandledRejection.js<br>import { useEffect, useState, useCallback } from &#39;react&#39;;<br><br>export function useUnhandledRejection() {<br>  const [lastError, setLastError] = useState(null);<br>  const handleRejection = useCallback((event) =&gt; {<br>    const message = event.reason?.message ?? String(event.reason);<br>    console.error(&#39;Unhandled promise rejection:&#39;, event.reason);<br>    setLastError(message);<br>    event.preventDefault();<br>  }, []);<br>  useEffect(() =&gt; {<br>    window.addEventListener(&#39;unhandledrejection&#39;, handleRejection);<br>    return () =&gt; window.removeEventListener(&#39;unhandledrejection&#39;, handleRejection);<br>  }, [handleRejection]);<br>  return lastError;<br>}</pre><p>And a component that uses it:</p><pre>// ErrorBanner.jsx<br>import { useUnhandledRejection } from &#39;./useUnhandledRejection&#39;;<br><br>export function ErrorBanner() {<br>  const lastError = useUnhandledRejection();<br>  if (!lastError) return null;<br>  return &lt;div className=&quot;error-banner&quot;&gt;{lastError}&lt;/div&gt;;<br>}</pre><p>Mount &lt;ErrorBanner /&gt; once, near the top of your component tree, and every unhandled rejection anywhere in the app surfaces through it.</p><h3>Unit Testing the React Version</h3><p>Using React Testing Library, you dispatch the same kind of synthetic event and assert the banner shows up:</p><pre>// ErrorBanner.test.jsx<br>import { render, screen, act } from &#39;@testing-library/react&#39;;<br>import { ErrorBanner } from &#39;./ErrorBanner&#39;;<br><br>test(&#39;shows the error banner after an unhandled promise rejection&#39;, () =&gt; {<br>  render(&lt;ErrorBanner /&gt;);<br>  act(() =&gt; {<br>    const event = new Event(&#39;unhandledrejection&#39;);<br>    event.reason = new Error(&#39;Something broke&#39;);<br>    window.dispatchEvent(event);<br>  });<br>  expect(screen.getByText(&#39;Something broke&#39;)).toBeInTheDocument();<br>});</pre><p>One gotcha worth flagging here: if you’re running tests under jsdom, its support for a real PromiseRejectionEvent constructor is limited, which is why both examples above build a plain Event and manually attach reason rather than trying to construct the browser-native event class directly.</p><h3>Bonus Tips</h3><p>A few things that tend to separate a “works in the demo” version of this from one that actually holds up in production:</p><ul><li>Call event.preventDefault() only after you&#39;ve logged the error somewhere durable — it suppresses the browser&#39;s own default console warning, which is convenient once you have your own logging in place, but a footgun if you add it before you&#39;re actually capturing anything.</li><li>Pair unhandledrejection with window.onerror (or a window.addEventListener(&#39;error&#39;, ...) listener) so you&#39;re covering both synchronous throws and rejected promises — they are genuinely separate mechanisms.</li><li>Listen for rejectionhandled too if you&#39;re building anything resembling real error monitoring, so a late .catch() doesn&#39;t get logged as a permanent failure.</li><li>If any part of your stack runs in Node (an SSR layer, an API route, a script), remember the API there is different: process.on(&#39;unhandledRejection&#39;, handler), not window.</li><li>Route the captured error into whatever tool you already use for monitoring (Sentry, LogRocket, your own backend) rather than leaving it in console.error — the whole point of this pattern is that a developer isn&#39;t watching the console when it happens.</li></ul><h3>Recap</h3><p>Unhandled promise rejections fail silently by design, and neither Angular’s zone patching nor React’s rendering model catches them for you automatically — you have to opt in with a global window listener. Once that listener is in place, wrapping it in a service (Angular) or a hook (React) makes it reusable, testable, and easy to hook into whatever logging or monitoring tool you already rely on.</p><p>Did this approach match how you’re already solving it, or do you have a different take? Drop a comment — I genuinely read every single one.</p><p><strong>Found this helpful?</strong><br> If this saved you even a few minutes of debugging or confusion, hit that clap button so others can find it too. It really does make a difference.</p><p><strong>Want more tips like this?</strong><br> I share one practical dev insight every week. Follow me here on Medium or subscribe to my newsletter so you never miss one.</p><p>Let’s connect — find me on LinkedIn or GitHub and let’s keep the conversation going.</p><h3>Follow Me for More Angular &amp; Frontend Goodness:</h3><p>I regularly share hands-on tutorials, clean code tips, scalable frontend architecture, and real-world problem-solving guides.</p><ul><li>💼 <a href="https://www.linkedin.com/in/errajatmalik/"><strong>LinkedIn</strong></a> — Let’s connect professionally</li><li>🎥 <a href="https://www.threads.net/@er.rajatmalik"><strong>Threads</strong></a> — Short-form frontend insights</li><li>🐦 <a href="https://twitter.com/er_rajatmalik"><strong>X (Twitter)</strong></a> — Developer banter + code snippets</li><li>👥 <a href="http://devrajat.bsky.social/"><strong>BlueSky</strong></a> — Stay up to date on frontend trends</li><li>🌟 <a href="https://github.com/malikrajat"><strong>GitHub Projects</strong></a> — Explore code in action</li><li>🌐 <a href="https://malikrajat.github.io/"><strong>Website</strong></a> — Everything in one place</li><li>📚 <a href="https://medium.com/@codewithrajat/"><strong>Medium Blog</strong></a> — Long-form content and deep-dives</li><li>💬 <a href="https://dev.to/codewithrajat"><strong>Dev Blog</strong></a> — Free Long-form content and deep-dives</li><li>✉️ <a href="https://codewithrajat.substack.com/"><strong>Substack</strong></a> — Weekly frontend stories &amp; curated resources</li><li>🧩 <a href="https://rajatmalik.dev/"><strong>Portfolio</strong></a> — Projects, talks, and recognitions</li><li>✍️ <a href="https://hashnode.com/@codeswithrajat"><strong>Hashnode</strong></a> — Developer blog posts &amp; tech discussions</li><li>✍️ <a href="https://www.reddit.com/user/codewithrajat/submitted/">**Reddit</a>** — Developer blog posts &amp; tech discussions</li></ul><h3>Before you go</h3><ul><li>Please take a moment to like the post and follow the writer!</li><li>Did you know that over 400,000 developers share what they’re building, learning, and discovering across our platforms every month? Learn how you can contribute <a href="https://plainenglish.io/write-for-us">here</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc321b4e237c" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/stop-losing-errors-to-the-void-handling-unhandled-promise-rejections-globally-in-javascript-cc321b4e237c">Stop Losing Errors to the Void: Handling Unhandled Promise Rejections Globally in JavaScript…</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>