{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreicfukmubgxkxmjapv5ffvsmiyarhuqtjdjt5v6wuybvcetcnusby4",
    "uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpwlcrc32yn2"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreidxqmd435kcrw2plsnacn4borvyp5zc72vwwizofcep576jm3f4gy"
    },
    "mimeType": "image/webp",
    "size": 66182
  },
  "path": "/straccia17/from-angularjs-to-fine-grained-reactivity-part-2-the-js-proxy-runtime-2m60",
  "publishedAt": "2026-07-05T21:55:01.000Z",
  "site": "https://dev.to",
  "tags": [
    "angular",
    "frontend",
    "javascript",
    "webdev",
    "LinkedIn"
  ],
  "textContent": "In the first article of this series, we saw how a custom build-time compiler can transform a legacy Angular.js template into raw, optimized JavaScript.\n\nTo recap, starting from this template:\n\n\n\n    <!-- simple.html -->\n    <p>Hello {{ name }}!</p>\n\n\nOur Go compiler generates the following JavaScript module:\n\n\n\n    // simple.js\n    export function template() {\n        const p_0 = document.createElement(\"p\");\n        const text_1 = document.createTextNode(\"\");\n        p_0.append(text_1);\n\n        return {\n            mount(container) {\n                container.append(p_0);\n            },\n            update(change) {\n                if (\"name\" in change) {\n                    text_1.data = \"Hello \" + change.name + \"!\";\n                }\n            }\n        }\n    }\n\n\nThis is incredibly clean. By running `template()`, we get an object with `mount` and `update` methods.\n\nUsing `mount` is fully intuitive: we pass a reference to a DOM element, and it injects our empty paragraph (`p_0`) into it:\n\n\n\n    import { template } from './simple.js';\n\n    const { mount, update } = template();\n    const container = document.getElementById('view-container');\n\n    mount(container);\n    // The DOM now contains: <p></p> (waiting for data)\n\n\nHowever, the paragraph remains empty until we call `update` with a change object like this:\n\n\n\n    let changes = {\n        name: \"Mario\",\n    };\n\n    update(changes);\n    // The DOM surgically updates to: <p>Hello Mario!</p>\n\n\nBut who is responsible for tracking changes in our application state, building this `changes` object, and calling `update`?\n\nThe answer lies in marrying the legacy Angular.js `$scope` with the modern **JavaScript Proxy API**.\n\n##  The Legacy State Pattern\n\nIn a traditional Angular.js application, developers mutate the state directly inside a controller by assigning properties to the `$scope` object:\n\n\n\n    // simple-controller.js\n    export function SimpleController($scope) {\n        $scope.name = \"Mario\";\n    }\n\n\nTo bridge the gap between this legacy controller and our new build-time template, we need a way to automatically capture the assignment `$scope.name = \"Mario\"` and translate it into a structured update:\n\n\n\n    let changes = {\n        name: \"Mario\"\n    };\n\n\nInstead of running a heavy runtime digest cycle to dirty-check the entire scope, we can intercept these mutations at the exact moment they happen. This is where **Proxies** shine.\n\n##  How the JavaScript Proxy API Saves the Day\n\nThe `Proxy` object allows us to wrap a target object and intercept fundamental operations, such as property lookups, assignments, and function invocations.\n\nBy wrapping our `$scope` in a Proxy before passing it to the controller, we can execute custom code whenever a property is set.\n\nLet's look at how we can implement a basic `set` trap:\n\n\n\n    import { SimpleController } from './simple-controller.js';\n\n    // Define a handler with a \"set\" trap\n    const handler = {\n        set(target, prop, value) {\n            console.log(`Property \"${prop}\" changed to: ${value}`);\n\n            // Actually set the value on the target object\n            target[prop] = value;\n\n            // The set trap must return true in strict mode\n            return true;\n        }\n    };\n\n    // Wrap an empty object with our Proxy handler\n    const $scope = new Proxy({}, handler);\n\n    // Run the legacy controller with our reactive scope\n    SimpleController($scope);\n    // Console logs: Property \"name\" changed to: Mario\n\n\nEvery time the controller executes `$scope.name = \"Mario\"`, our Proxy intercepts the assignment. We now have a lightweight, non-invasive mechanism to capture state mutations in real time.\n\n##  Putting It All Together: The Runtime Connection\n\nNow we can connect our Proxy-based `$scope` directly to the `update` method generated by our compiler.\n\nHere is the complete runtime implementation:\n\n\n\n    import { SimpleController } from './simple-controller.js';\n    import { template } from './simple.js';\n\n    // 1. Initialize the compiled template and mount it\n    const { mount, update } = template();\n    const container = document.getElementById('view-container');\n    mount(container);\n\n    // 2. Create the reactive $scope using a Proxy\n    const $scope = new Proxy({}, {\n        set(target, prop, value) {\n            // Intercept mutation, build the change object, and trigger the DOM update\n            update({ [prop]: value });\n\n            // Propagate the change to the underlying object using Reflect\n            return Reflect.set(target, prop, value);\n        }\n    });\n\n    // 3. Execute the controller to trigger the initial render\n    SimpleController($scope);\n\n\n###  The result:\n\n  1. The controller runs and executes `$scope.name = \"Mario\"`.\n\n  2. The Proxy intercepts the write and immediately triggers `update({ name: \"Mario\" })`.\n\n  3. The compiled `update` function surgically targets the `TextNode` and updates the text to `\"Hello Mario!\"`.\n\n  4. **Zero dirty-checking, zero Virtual DOM, zero external dependencies.**\n\n\n\n\n##  What’s Next?\n\nWhile this reactive loop is extremely elegant, real-world enterprise applications are rarely this simple.\n\nWhat happens when a controller updates multiple properties in a row? In our current basic implementation, changing three properties sequentially would trigger three immediate, synchronous DOM repaints. To prevent layout thrashing, we need to implement a **batching mechanism** to queue updates and flush them once per frame.\n\nFurthermore, how do we handle nested objects, arrays, and dependency tracking (Signals)?\n\nIn the next part of this series, we will explore how we scaled this runtime architecture to handle production-grade state management. Stay tuned!\n\n_Thanks for reading! I’m a Frontend Architect passionate about compilers, reactivity, and performance. Let's connect on LinkedIn to stay updated with the next parts of this journey._",
  "title": "From Angular.js to Fine-Grained Reactivity: Part 2 — The JS Proxy Runtime"
}