{"id":543,"date":"2016-11-08T15:40:10","date_gmt":"2016-11-08T14:40:10","guid":{"rendered":"https:\/\/blog.risingstack.comwriting-a-javascript-framework-data-binding-es6-proxy\/"},"modified":"2024-05-30T00:08:56","modified_gmt":"2024-05-29T22:08:56","slug":"writing-a-javascript-framework-data-binding-es6-proxy","status":"publish","type":"post","link":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/","title":{"rendered":"Writing a JavaScript Framework &#8211; Data Binding with ES6 Proxies"},"content":{"rendered":"\n<\/p>\n\n\n\n<p><strong>This is the fifth chapter of the Writing a JavaScript framework series. In this chapter, I am going to explain how to create a simple, yet powerful data binding library with the new ES6 Proxies.<\/strong><\/p>\n\n\n\n<p><em>The series is about an open-source client-side framework, called NX. During the series, I explain the main difficulties I had to overcome while writing the framework. If you are interested in NX please visit the&nbsp;home page.<\/em><\/p>\n\n\n\n<p>The series includes the following chapters:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li><a href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-project-structuring\/\" target=\"_blank\" rel=\"noreferrer noopener\">Project structuring<\/a><\/li><li><a href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-execution-timing-beyond-settimeout\/\" target=\"_blank\" rel=\"noreferrer noopener\">Execution timing<\/a><\/li><li><a href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-sandboxed-code-evaluation\/\" target=\"_blank\" rel=\"noreferrer noopener\">Sandboxed code evaluation<\/a><\/li><li><a href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-dirty-checking\/\" target=\"_blank\" rel=\"noreferrer noopener\">Data binding introduction<\/a><\/li><li>Data Binding with ES6 Proxies (current chapter)<\/li><li><a href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-the-benefits-of-custom-elements\/\" target=\"_blank\" rel=\"noreferrer noopener\">Custom elements<\/a><\/li><li><a href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-client-side-routing\/\" target=\"_blank\" rel=\"noreferrer noopener\">Client-side routing<\/a><\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<p>ES6 made JavaScript a lot more elegant, but the bulk of new features are just syntactic sugar. Proxies are one of the few non polyfillable additions. If you are not familiar with them, please take a quick look at the&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Proxy\" target=\"_blank\" rel=\"noreferrer noopener\">MDN Proxy docs<\/a>&nbsp;before going on.<\/p>\n\n\n\n<p>Having a basic knowledge of the ES6&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Reflect\" target=\"_blank\" rel=\"noreferrer noopener\">Reflection API<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Set\" target=\"_blank\" rel=\"noreferrer noopener\">Set<\/a>,&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\" target=\"_blank\" rel=\"noreferrer noopener\">Map<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/WeakMap\" target=\"_blank\" rel=\"noreferrer noopener\">WeakMap<\/a>&nbsp;objects will also be helpful.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"thenxobservelibrary\">The nx-observe library<\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/RisingStack\/nx-observe\" target=\"_blank\" rel=\"noreferrer noopener\">nx-observe<\/a>&nbsp;is a data binding solution in under 140 lines of code. It exposes the&nbsp;<code>observable(obj)<\/code>&nbsp;and&nbsp;<code>observe(fn)<\/code>&nbsp;functions, which are used to create observable objects and observer functions. An observer function automatically executes when an observable property used by it changes. The example below demonstrates this.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/\/ this is an observable object<\/em>\nconst person = observable({name: 'John', age: 20})\n\nfunction print () {\n  console.log(`${person.name}, ${person.age}`)\n}\n\n<em>\/\/ this creates an observer function<\/em>\n<em>\/\/ outputs 'John, 20' to the console<\/em>\nobserve(print)\n\n<em>\/\/ outputs 'Dave, 20' to the console<\/em>\nsetTimeout(() =&gt; person.name = 'Dave', 100)\n\n<em>\/\/ outputs 'Dave, 22' to the console<\/em>\nsetTimeout(() =&gt; person.age = 22, 200)\n<\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>print<\/code>&nbsp;function passed to&nbsp;<code>observe()<\/code>&nbsp;reruns every time&nbsp;<code>person.name<\/code>&nbsp;or&nbsp;<code>person.age<\/code>&nbsp;changes.&nbsp;<code>print<\/code>&nbsp;is called an observer function.<\/p>\n\n\n\n<p>If you are interested in a few more examples, please check the&nbsp;<a href=\"https:\/\/github.com\/RisingStack\/nx-observe#example\" target=\"_blank\" rel=\"noreferrer noopener\">GitHub readme<\/a>&nbsp;or the&nbsp;NX home page&nbsp;for a more lifelike scenario.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"implementingasimpleobservable\">Implementing a simple observable<\/h2>\n\n\n\n<p>In this section, I am going to explain what happens under the hood of nx-observe. First, I will show you how changes to an observable&#8217;s properties are detected and paired with observers. Then I will explain a way to run the observer functions triggered by these changes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"registeringchanges\">Registering changes<\/h3>\n\n\n\n<p>Changes are registered by wrapping observable objects into ES6 Proxies. These proxies seamlessly intercept get and set operations with the help of the&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Reflect\" target=\"_blank\" rel=\"noreferrer noopener\">Reflection API<\/a>.<\/p>\n\n\n\n<p>The variables&nbsp;<code>currentObserver<\/code>&nbsp;and&nbsp;<code>queueObserver()<\/code>&nbsp;are used in the code below, but will only be explained in the next section. For now, it is enough to know that&nbsp;<code>currentObserver<\/code>&nbsp;always points to the currently executing observer function, and&nbsp;<code>queueObserver()<\/code>&nbsp;is a function that queues an observer to be executed soon.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n<em>\/* maps observable properties to a Set of\nobserver functions, which use the property *\/<\/em>\nconst observers = new WeakMap()\n\n<em>\/* points to the currently running \nobserver function, can be undefined *\/<\/em>\nlet currentObserver\n\n<em>\/* transforms an object into an observable \nby wrapping it into a proxy, it also adds a blank\nMap for property-observer pairs to be saved later *\/<\/em>\nfunction observable (obj) {\n  observers.set(obj, new Map())\n  return new Proxy(obj, {get, set})\n}\n\n\/* this trap intercepts get operations,\nit does nothing if no observer is executing\nat the moment *\/\nfunction get (target, key, receiver) {\n  const result = Reflect.get(target, key, receiver)\n   if (currentObserver) {\n     registerObserver(target, key, currentObserver)\n   }\n  return result\n}\n\n<em>\/* if an observer function is running currently,\nthis function pairs the observer function \nwith the currently fetched observable property\nand saves them into the observers Map *\/<\/em>\nfunction registerObserver (target, key, observer) {\n  let observersForKey = observers.get(target).get(key)\n  if (!observersForKey) {\n    observersForKey = new Set()\n    observers.get(target).set(key, observersForKey)\n  }\n  observersForKey.add(observer)\n}\n\n<em>\/* this trap intercepts set operations,\nit queues every observer associated with the\ncurrently set property to be executed later *\/<\/em>\nfunction set (target, key, value, receiver) {\n  const observersForKey = observers.get(target).get(key)\n  if (observersForKey) {\n    observersForKey.forEach(queueObserver)\n  }\n  return Reflect.set(target, key, value, receiver)\n}\n<\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>get<\/code>&nbsp;trap does nothing if&nbsp;<code>currentObserver<\/code>&nbsp;is not set. Otherwise, it pairs the fetched observable property and the currently running observer and saves them into the&nbsp;<code>observers<\/code>&nbsp;WeakMap. Observers are saved into a&nbsp;<code>Set<\/code>&nbsp;per observable property. This ensures that there are no duplicates.<\/p>\n\n\n\n<p>The&nbsp;<code>set<\/code>&nbsp;trap is retrieving all the observers paired with the modified observable property and queues them for later execution.<\/p>\n\n\n\n<p>You can find a figure and a step-by-step description explaining the nx-observe example code below.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/blog-assets.risingstack.com\/2016\/11\/writing-a-javascript-framework-data-binding-with-es6-proxy-observables-code.png\" alt=\"JavaScript data binding with es6 proxy - observable code sample\"\/><\/figure>\n\n\n\n<ol class=\"wp-block-list\"><li>The&nbsp;<code>person<\/code>&nbsp;observable object is created.<\/li><li><code>currentObserver<\/code>&nbsp;is set to&nbsp;<code>print<\/code>.<\/li><li><code>print<\/code>&nbsp;starts executing.<\/li><li><code>person.name<\/code>&nbsp;is retrieved inside&nbsp;<code>print<\/code>.<\/li><li>The proxy&nbsp;<code>get<\/code>&nbsp;trap on&nbsp;<code>person<\/code>&nbsp;is invoked.<\/li><li>The observer Set belonging to the&nbsp;<code>(person, name)<\/code>&nbsp;pair is retrieved by&nbsp;<code>observers.get(person).get('name')<\/code>.<\/li><li><code>currentObserver<\/code>&nbsp;(print) is added to the observer Set.<\/li><li>Step 4-7 are executed again with&nbsp;<code>person.age<\/code>.<\/li><li><code>${person.name}, ${person.age}<\/code>&nbsp;is printed to the console.<\/li><li><code>print<\/code>&nbsp;finishes executing.<\/li><li><code>currentObserver<\/code>&nbsp;is set to undefined.<\/li><li>Some other code starts running.<\/li><li><code>person.age<\/code>&nbsp;is set to a new value (22).<\/li><li>The proxy&nbsp;<code>set<\/code>&nbsp;trap on&nbsp;<code>person<\/code>&nbsp;is invoked.<\/li><li>The observer Set belonging to the&nbsp;<code>(person, age)<\/code>&nbsp;pair is retrieved by&nbsp;<code>observers.get(person).get('age')<\/code>.<\/li><li>Observers in the observer Set (including&nbsp;<code>print<\/code>) are queued for execution.<\/li><li><code>print<\/code>&nbsp;executes again.<\/li><\/ol>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"runningtheobservers\">Running the observers<\/h3>\n\n\n\n<p>Queued observers run asynchronously in one batch, which results in superior performance. During registration, the observers are synchronously added to the&nbsp;<code>queuedObservers<\/code>&nbsp;<code>Set<\/code>. A&nbsp;<code>Set<\/code>&nbsp;cannot contain duplicates, so enqueuing the same observer multiple times won&#8217;t result in multiple executions. If the&nbsp;<code>Set<\/code>&nbsp;was empty before, a new task is scheduled to iterate and execute all the queued observers after some time.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/* contains the triggered observer functions,\nwhich should run soon *\/<\/em>\nconst queuedObservers = new Set()\n\n<em>\/* points to the currently running observer,\nit can be undefined *\/<\/em>\nlet currentObserver\n\n<em>\/* the exposed observe function *\/<\/em>\nfunction observe (fn) {\n  queueObserver(fn)\n}\n\n<em>\/* adds the observer to the queue and \nensures that the queue will be executed soon *\/<\/em>\nfunction queueObserver (observer) {\n  if (queuedObservers.size === 0) {\n    Promise.resolve().then(runObservers)\n  }\n  queuedObservers.add(observer)\n}\n\n<em>\/* runs the queued observers,\ncurrentObserver is set to undefined in the end *\/<\/em>\nfunction runObservers () {\n  try {\n    queuedObservers.forEach(runObserver)\n  } finally {\n    currentObserver = undefined\n    queuedObservers.clear()\n  }\n}\n\n<em>\/* sets the global currentObserver to observer, \nthen executes it *\/<\/em>\nfunction runObserver (observer) {\n  currentObserver = observer\n  observer()\n}\n<\/code><\/pre>\n\n\n\n<p>The code above ensures that whenever an observer is executing, the global&nbsp;<code>currentObserver<\/code>&nbsp;variable points to it. Setting&nbsp;<code>currentObserver<\/code>&nbsp;&#8216;switches&#8217; the&nbsp;<code>get<\/code>&nbsp;traps on, to listen and pair&nbsp;<code>currentObserver<\/code>&nbsp;with all the observable properties it uses while executing.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"buildingadynamicobservabletree\">Building a dynamic observable tree<\/h2>\n\n\n\n<p>So far our model works nicely with single level data structures but requires us to wrap every new object-valued property in an observable by hand. For example, the code below would not work as expected.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const person = observable({data: {name: 'John'}})\n\nfunction print () {\n  console.log(person.data.name)\n}\n\n<em>\/\/ outputs 'John' to the console<\/em>\nobserve(print)\n\n<em>\/\/ does nothing<\/em>\nsetTimeout(() =&gt; person.data.name = 'Dave', 100)\n<\/code><\/pre>\n\n\n\n<p>In order to make this code work, we would have to replace&nbsp;<code>observable({data: {name: 'John'}})<\/code>&nbsp;with&nbsp;<code>observable({data: observable({name: 'John'})})<\/code>. Fortunately we can eliminate this inconvenience by modifying the&nbsp;<code>get<\/code>&nbsp;trap a little bit.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function get (target, key, receiver) {\n  const result = Reflect.get(target, key, receiver)\n  if (currentObserver) {\n    registerObserver(target, key, currentObserver)\n    if (typeof result === 'object') {\n      const observableResult = observable(result)\n      Reflect.set(target, key, observableResult, receiver)\n      return observableResult\n    }\n  }\n  return result\n}\n<\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>get<\/code>&nbsp;trap above wraps the returned value into an observable proxy before returning it &#8211; in case it is an object. This is perfect from a performance point of view too, since observables are only created when they are really needed by an observer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"comparisonwithanes5technique\">Comparison with an ES5 technique<\/h2>\n\n\n\n<p>A very similar data binding technique can be implemented with ES5 property accessors (getter\/setter) instead of ES6 Proxies. Many popular libraries use this technique, for example&nbsp;<a href=\"https:\/\/mobxjs.github.io\/mobx\/\" target=\"_blank\" rel=\"noreferrer noopener\">MobX<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/vuejs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Vue<\/a>. Using proxies over accessors has two main advantages and a major disadvantage.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"expandoproperties\">Expando properties<\/h3>\n\n\n\n<p>Expando properties are dynamically added properties in JavaScript. The ES5 technique does not support expando properties since accessors have to be predefined per property to be able to intercept operations. This is a technical reason why central stores with a predefined set of keys are trending nowadays.<\/p>\n\n\n\n<p>On the other hand, the Proxy technique does support expando properties, since proxies are defined per object and they intercept operations for every property of the object.<\/p>\n\n\n\n<p>A typical example where expando properties are crucial is with using arrays. JavaScript arrays are pretty much useless without the ability to add or remove items from them. ES5 data binding techniques usually hack around this problem by providing custom or overwritten&nbsp;<code>Array<\/code>&nbsp;methods.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"gettersandsetters\">Getters and setters<\/h3>\n\n\n\n<p>Libraries using the ES5 method provide &#8216;computed&#8217; bound properties by some special syntax. These properties have their native equivalents, namely getters and setters. However the ES5 method uses getters\/setters internally to set up the data binding logic, so it can not work with property accessors.<\/p>\n\n\n\n<p>Proxies intercept every kind of property access and mutation, including getters and setters, so this does not pose a problem for the ES6 method.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"thedisadvantage\">The disadvantage<\/h3>\n\n\n\n<p>The big disadvantage of using Proxies is browser support. They are only supported in the&nbsp;<a href=\"http:\/\/caniuse.com\/#feat=proxy\" target=\"_blank\" rel=\"noreferrer noopener\">most recent browsers<\/a>&nbsp;and the best parts of the Proxy API are non polyfillable.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"afewnotes\">A few notes<\/h2>\n\n\n\n<p>The data binding method introduced here is a working one, but I made some simplifications to make it digestible. You can find a few notes below about the topics I left out because of this simplification.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"cleaningup\">Cleaning up<\/h3>\n\n\n\n<p>Memory leaks are nasty. The code introduced here avoids them in a sense, as it uses a&nbsp;<code>WeakMap<\/code>&nbsp;to save the observers. This means that the observers associated with an observable are garbage collected together with the observable.<\/p>\n\n\n\n<p>However, a possible use case could be a central, durable store with a frequently shifting DOM around it. In this case, DOM nodes should release all of their registered observers before they are garbage collected. This functionality is left out of the example, but you can check how the&nbsp;<code>unobserve()<\/code>&nbsp;function is implemented in the&nbsp;nx-observe code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"doublewrappingwithproxies\">Double wrapping with Proxies<\/h3>\n\n\n\n<p>Proxies are transparent, meaning there is no native way of determining if something is a Proxy or a plain object. Moreover, they can be nested infinitely, so without necessary precaution, we might end up wrapping an observable again and again.<\/p>\n\n\n\n<p>There are many clever ways to make a Proxy distinguishable from normal objects, but I left it out of the example. One way would be to add a Proxy to a&nbsp;<code>WeakSet<\/code>&nbsp;named&nbsp;<code>proxies<\/code>&nbsp;and check for inclusion later. If you are interested in how nx-observe implements the&nbsp;<code>isObservable()<\/code>&nbsp;method, please check the&nbsp;code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"inheritance\">Inheritance<\/h3>\n\n\n\n<p>nx-observe also works with prototypal inheritance. The example below demonstrates what does this mean exactly.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const parent = observable({greeting: 'Hello'})\nconst child = observable({subject: 'World!'})\nObject.setPrototypeOf(child, parent)\n\nfunction print () {\n  console.log(`${child.greeting} ${child.subject}`)\n}\n\n<em>\/\/ outputs 'Hello World!' to the console<\/em>\nobserve(print)\n\n<em>\/\/ outputs 'Hello There!' to the console<\/em>\nsetTimeout(() =&gt; child.subject = 'There!')\n\n<em>\/\/ outputs 'Hey There!' to the console<\/em>\nsetTimeout(() =&gt; parent.greeting = 'Hey', 100)\n\n<em>\/\/ outputs 'Look There!' to the console<\/em>\nsetTimeout(() =&gt; child.greeting = 'Look', 200)\n<\/code><\/pre>\n\n\n\n<p>The&nbsp;<code>get<\/code>&nbsp;operation is invoked for every member of the prototype chain until the property is found, so the observers are registered everywhere they could be needed.<\/p>\n\n\n\n<p>There are some edge cases caused by the little-known fact that&nbsp;<code>set<\/code>&nbsp;operations also walk the prototype chain (quite sneakily), but these won&#8217;t be covered here.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"internalproperties\">Internal properties<\/h3>\n\n\n\n<p>Proxies also intercept &#8216;internal property access&#8217;. Your code probably uses many internal properties that you usually don&#8217;t even think about. Some keys for such properties are the&nbsp;<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Symbol\" target=\"_blank\" rel=\"noreferrer noopener\">well-known Symbols<\/a>&nbsp;for example. Properties like these are usually correctly intercepted by Proxies, but there are a few buggy cases.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"asynchronousnature\">Asynchronous nature<\/h3>\n\n\n\n<p>The observers could be run synchronously when the&nbsp;<code>set<\/code>&nbsp;operation is intercepted. This would provide several advantages like less complexity, predictable timing and nicer stack traces, but it would also cause a big mess for certain scenarios.<\/p>\n\n\n\n<p>Imagine pushing 1000 items to an observable array in a single loop. The array length would change a 1000 times and the observers associated with it would also execute a 1000 times in quick succession. This means running the exact same set of functions a 1000 times, which is rarely a useful thing.<\/p>\n\n\n\n<p>Another problematic scenario would be two-way observations. The below code would start an infinite cycle if observers ran synchronously.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const observable1 = observable({prop: 'value1'})\nconst observable2 = observable({prop: 'value2'})\n\nobserve(() =&gt; observable1.prop = observable2.prop)\nobserve(() =&gt; observable2.prop = observable1.prop)\n<\/code><\/pre>\n\n\n\n<p>For these reasons nx-observe queues observers without duplicates and executes them in one batch as a microtask to avoid&nbsp;<a href=\"https:\/\/en.wikipedia.org\/wiki\/Flash_of_unstyled_content\" target=\"_blank\" rel=\"noreferrer noopener\">FOUC<\/a>. If you are unfamiliar with the concept of a microtask, please check my&nbsp;<a href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-execution-timing-beyond-settimeout\/\" target=\"_blank\" rel=\"noreferrer noopener\">previous article<\/a>&nbsp;about timing in the browser.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"databindingwithes6proxiestheconclusion\">Data binding with ES6 Proxies &#8211; the Conclusion<\/h2>\n\n\n\n<p>If you are interested in the NX framework, please visit the&nbsp;home page. Adventurous readers can find the NX source code in&nbsp;<a href=\"https:\/\/github.com\/RisingStack\/nx-framework\" target=\"_blank\" rel=\"noreferrer noopener\">this Github repository<\/a>&nbsp;and the nx-observe source code in&nbsp;<a href=\"https:\/\/github.com\/RisingStack\/nx-observe\" target=\"_blank\" rel=\"noreferrer noopener\">this Github repository<\/a>.<\/p>\n\n\n\n<p>I hope you found this a good read, see you next time when weI\u2019ll discuss custom HTML Elements!<\/p>\n\n\n\n<p>If you have any thoughts on the topic, please share them in the comments.<\/p>\n\n\n\n<p>\n","protected":false},"excerpt":{"rendered":"<p>This article explains how you can create a simple, yet powerful JavaScript data binding library with the new ES6 Proxies.<\/p>\n","protected":false},"author":7,"featured_media":544,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[27],"tags":[14,12],"class_list":["post-543","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-javascript","tag-edited","tag-temp-use-only"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Writing a JavaScript Framework - Data Binding with ES6 Proxies - RisingStack Engineering<\/title>\n<meta name=\"description\" content=\"This article explains how you can create a simple, yet powerful JavaScript data binding library with the new ES6 Proxies.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Writing a JavaScript Framework - Data Binding with ES6 Proxies - RisingStack Engineering\" \/>\n<meta property=\"og:description\" content=\"This article explains how you can create a simple, yet powerful JavaScript data binding library with the new ES6 Proxies.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/\" \/>\n<meta property=\"og:site_name\" content=\"RisingStack Engineering\" \/>\n<meta property=\"article:published_time\" content=\"2016-11-08T14:40:10+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-05-29T22:08:56+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/07\/writing-a-javascript-framework-data-binding-es6-proxy-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"bertalan\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"bertalan\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/\"},\"author\":{\"name\":\"bertalan\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#\\\/schema\\\/person\\\/c396109d1854b5194e23172e95965df6\"},\"headline\":\"Writing a JavaScript Framework &#8211; Data Binding with ES6 Proxies\",\"datePublished\":\"2016-11-08T14:40:10+00:00\",\"dateModified\":\"2024-05-29T22:08:56+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/\"},\"wordCount\":1726,\"publisher\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog.risingstack.com\\\/wp-content\\\/uploads\\\/2021\\\/07\\\/writing-a-javascript-framework-data-binding-es6-proxy-1.png\",\"keywords\":[\"edited\",\"reviewed\"],\"articleSection\":[\"JavaScript\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/\",\"url\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/\",\"name\":\"Writing a JavaScript Framework - Data Binding with ES6 Proxies - RisingStack Engineering\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/blog.risingstack.com\\\/wp-content\\\/uploads\\\/2021\\\/07\\\/writing-a-javascript-framework-data-binding-es6-proxy-1.png\",\"datePublished\":\"2016-11-08T14:40:10+00:00\",\"dateModified\":\"2024-05-29T22:08:56+00:00\",\"description\":\"This article explains how you can create a simple, yet powerful JavaScript data binding library with the new ES6 Proxies.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/#primaryimage\",\"url\":\"https:\\\/\\\/blog.risingstack.com\\\/wp-content\\\/uploads\\\/2021\\\/07\\\/writing-a-javascript-framework-data-binding-es6-proxy-1.png\",\"contentUrl\":\"https:\\\/\\\/blog.risingstack.com\\\/wp-content\\\/uploads\\\/2021\\\/07\\\/writing-a-javascript-framework-data-binding-es6-proxy-1.png\",\"width\":1200,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/writing-a-javascript-framework-data-binding-es6-proxy\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blog.risingstack.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Writing a JavaScript Framework &#8211; Data Binding with ES6 Proxies\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#website\",\"url\":\"https:\\\/\\\/blog.risingstack.com\\\/\",\"name\":\"RisingStack Engineering\",\"description\":\"Node.js Tutorials &amp; Resources\",\"publisher\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blog.risingstack.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#organization\",\"name\":\"RisingStack Engineering\",\"url\":\"https:\\\/\\\/blog.risingstack.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/blog.risingstack.com\\\/wp-content\\\/uploads\\\/2021\\\/05\\\/logo-white-f0fca36fce94ceacd3d608caa6649361-1.svg\",\"contentUrl\":\"https:\\\/\\\/blog.risingstack.com\\\/wp-content\\\/uploads\\\/2021\\\/05\\\/logo-white-f0fca36fce94ceacd3d608caa6649361-1.svg\",\"width\":180,\"height\":45,\"caption\":\"RisingStack Engineering\"},\"image\":{\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blog.risingstack.com\\\/#\\\/schema\\\/person\\\/c396109d1854b5194e23172e95965df6\",\"name\":\"bertalan\",\"sameAs\":[\"https:\\\/\\\/twitter.com\\\/solkimicreb1\"],\"url\":\"https:\\\/\\\/blog.risingstack.com\\\/author\\\/bertalan\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Writing a JavaScript Framework - Data Binding with ES6 Proxies - RisingStack Engineering","description":"This article explains how you can create a simple, yet powerful JavaScript data binding library with the new ES6 Proxies.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/","og_locale":"en_US","og_type":"article","og_title":"Writing a JavaScript Framework - Data Binding with ES6 Proxies - RisingStack Engineering","og_description":"This article explains how you can create a simple, yet powerful JavaScript data binding library with the new ES6 Proxies.","og_url":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/","og_site_name":"RisingStack Engineering","article_published_time":"2016-11-08T14:40:10+00:00","article_modified_time":"2024-05-29T22:08:56+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/07\/writing-a-javascript-framework-data-binding-es6-proxy-1.png","type":"image\/png"}],"author":"bertalan","twitter_card":"summary_large_image","twitter_misc":{"Written by":"bertalan","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/#article","isPartOf":{"@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/"},"author":{"name":"bertalan","@id":"https:\/\/blog.risingstack.com\/#\/schema\/person\/c396109d1854b5194e23172e95965df6"},"headline":"Writing a JavaScript Framework &#8211; Data Binding with ES6 Proxies","datePublished":"2016-11-08T14:40:10+00:00","dateModified":"2024-05-29T22:08:56+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/"},"wordCount":1726,"publisher":{"@id":"https:\/\/blog.risingstack.com\/#organization"},"image":{"@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/07\/writing-a-javascript-framework-data-binding-es6-proxy-1.png","keywords":["edited","reviewed"],"articleSection":["JavaScript"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/","url":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/","name":"Writing a JavaScript Framework - Data Binding with ES6 Proxies - RisingStack Engineering","isPartOf":{"@id":"https:\/\/blog.risingstack.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/#primaryimage"},"image":{"@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/07\/writing-a-javascript-framework-data-binding-es6-proxy-1.png","datePublished":"2016-11-08T14:40:10+00:00","dateModified":"2024-05-29T22:08:56+00:00","description":"This article explains how you can create a simple, yet powerful JavaScript data binding library with the new ES6 Proxies.","breadcrumb":{"@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/#primaryimage","url":"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/07\/writing-a-javascript-framework-data-binding-es6-proxy-1.png","contentUrl":"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/07\/writing-a-javascript-framework-data-binding-es6-proxy-1.png","width":1200,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/blog.risingstack.com\/writing-a-javascript-framework-data-binding-es6-proxy\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.risingstack.com\/"},{"@type":"ListItem","position":2,"name":"Writing a JavaScript Framework &#8211; Data Binding with ES6 Proxies"}]},{"@type":"WebSite","@id":"https:\/\/blog.risingstack.com\/#website","url":"https:\/\/blog.risingstack.com\/","name":"RisingStack Engineering","description":"Node.js Tutorials &amp; Resources","publisher":{"@id":"https:\/\/blog.risingstack.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.risingstack.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/blog.risingstack.com\/#organization","name":"RisingStack Engineering","url":"https:\/\/blog.risingstack.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.risingstack.com\/#\/schema\/logo\/image\/","url":"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/05\/logo-white-f0fca36fce94ceacd3d608caa6649361-1.svg","contentUrl":"https:\/\/blog.risingstack.com\/wp-content\/uploads\/2021\/05\/logo-white-f0fca36fce94ceacd3d608caa6649361-1.svg","width":180,"height":45,"caption":"RisingStack Engineering"},"image":{"@id":"https:\/\/blog.risingstack.com\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/blog.risingstack.com\/#\/schema\/person\/c396109d1854b5194e23172e95965df6","name":"bertalan","sameAs":["https:\/\/twitter.com\/solkimicreb1"],"url":"https:\/\/blog.risingstack.com\/author\/bertalan\/"}]}},"_links":{"self":[{"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/posts\/543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/comments?post=543"}],"version-history":[{"count":1,"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/posts\/543\/revisions"}],"predecessor-version":[{"id":4288,"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/posts\/543\/revisions\/4288"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/media\/544"}],"wp:attachment":[{"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/media?parent=543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/categories?post=543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.risingstack.com\/wp-json\/wp\/v2\/tags?post=543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}