What Does The Dot Do In JavaScript? - Jim Fisher

What does the dot do in JavaScript?

Ah, that little dot! Every line of JavaScript you write contains something like foo.bar, foo.bar(), or foo.bar = baz. But do you know what it does? Two concepts interact subtly here: prototypical inheritance and accessor properties (getters/setters). There are some weird corner cases, and I bet you don’t know all of them! In this post series, I explain the behavior of foo.bar, foo.bar(), and foo.bar = baz by reimplementing them in plain JavaScript.

First off, foo.bar, foo.bar(x,y), and foo.bar = baz are all “just” syntax sugar for the foo['bar'] syntax form:

x = foo.bar; /* is sugar for */ x = foo['bar']; foo.bar = baz; /* is sugar for */ foo['bar'] = baz; foo.bar(x,y); /* is sugar for */ foo['bar'](x,y);

Unfortunately, it remains to explain foo[bar], foo[bar](x,y) and foo[bar] = baz, and these are not trivial. I actually had to consult the frumpy ECMAScript spec to get a reasonable description. In the spec, these forms are called Get, Set, and Invoke. Let’s re-implement them as JavaScript functions. First, here’s Get:

// You should be able to replace `obj[prop]` with `Get(obj,prop)` function Get(obj, prop) { prop = typeof prop === 'symbol' ? prop : String(prop); for (let ancestor = obj; ancestor !== null; ancestor = Object.getPrototypeOf(ancestor)) { const desc = Object.getOwnPropertyDescriptor(ancestor, prop); if (desc) { return desc.get ? desc.get.call(obj) : desc.set ? undefined // Accessor property with just a setter : desc.value; // Assuming data property } } return undefined; }

Surprisingly, if we find an accessor with no getter, we just return undefined, rather than searching further up the chain:

const parent = { x: 5 }; const child = { set x(newX) { this.x = newX; } }; Object.setPrototypeOf(child, parent); console.log(child.x) // Logs undefined, not 5!

Next, let’s reimplement the “method call” syntax, foo[bar](x,y). This is mercifully short, because it re-uses the Get function:

// You should be able to replace `obj[prop](x,y)` with `Invoke(obj,prop, [x,y])`. // Idiomatic adaptation of https://www.ecma-international.org/ecma-262/10.0/index.html#sec-invoke function Invoke(obj, prop, argumentsList = []) { const method = Get(obj, prop); if (!(method instanceof Function)) throw new TypeError(`someObj.${prop} is not a function`); return method.call(obj, ...argumentsList); }

Surprisingly, this means that you can define a getter that returns the function to bind in the method call:

const obj = { i: 10, get add() { // Return the method to be called return function(x) { this.i += x; // `this` will be `obj` }; } }; obj.add(2); console.log(obj.i); // Logs 12

Finally, the worst of the bunch: foo[bar] = baz. This is called Set in the spec. It’s full of corner cases. Here’s my attempt at a reimplementation in plain JavaScript:

// You should be able to replace `obj[prop] = x` with `Set(obj,prop,x)`. // Idiomatic adaptation of https://www.ecma-international.org/ecma-262/10.0/index.html#sec-set-o-p-v-throw // and https://www.ecma-international.org/ecma-262/10.0/index.html#sec-property-accessors export function Set(obj, prop, val) { prop = typeof prop === 'symbol' ? prop : String(prop); // First, try to update existing own property. const ownPropDesc = Object.getOwnPropertyDescriptor(obj, prop); if (ownPropDesc) { if (ownPropDesc.set) { ownPropDesc.set.call(obj, val); return val; } else if (ownPropDesc.get) { // Note: we end here, rather than going up the chain looking for a setter. throw new TypeError(`Cannot set property ${prop} of #<Object> which has only a getter`); } else { // must be data prop; update it Object.defineProperty(obj, prop, { value: val }); return val; } } else { // Not an own property. Search the prototype chain. for (let ancestor = Object.getPrototypeOf(obj); ancestor !== null; ancestor = Object.getPrototypeOf(ancestor)) { const ancestorPropDesc = Object.getOwnPropertyDescriptor(ancestor, prop); if (ancestorPropDesc) { if (ancestorPropDesc.set) { ancestorPropDesc.set.call(obj, val); return val; } else if (ancestorPropDesc.get) { throw new TypeError(`Cannot set property ${prop} of #<Object> which has only a getter`); } else { // must be data prop if (ancestorPropDesc.writable) { // Note: despite the writable check, we _don't_ write to the ancestor, or continue up the chain. Object.defineProperty(obj, prop, { value: val, writable: true, enumerable: true, configurable: true }); return val; } else { throw new TypeError(`Cannot assign to read only property '${prop}' of object '#<Object>'`); } } } } // Not on the prototype chain either. Just set a new own property. Object.defineProperty(obj, prop, { value: val, writable: true, enumerable: true, configurable: true }); return val; } }

Before ES5 created accessor properties, the foo[bar] = baz notation would basically just set an own property. It would not traverse the prototype chain. But now, if there is no own property, it has to go up the chain looking for a potential setter function. If it doesn’t find find one, it falls back to creating a new own property.

Here’s the next post on how prototypes work.

Tagged #programming, #javascript, #fave.

Similar posts

The hacker hype cycle I got started with simple web development, but because enamored with increasingly esoteric programming concepts, leading to a “trough of hipster technologies” before returning to more productive work. 2019-03-23How Hacker News stays interesting Hacker News buried my post on conspiracy theories in my family due to overheated discussion, not censorship. Moderation keeps the site focused on interesting technical content. 2019-01-26My parents are Flat-Earthers For decades, my parents have been working up to Flat-Earther beliefs. From Egyptology to Jehovah’s Witnesses to theories that human built the Moon billions of years in the future. Surprisingly, it doesn’t affect their successful lives very much. For me, it’s a fun family pastime. 2019-01-20I hate telephones I hate telephones. Some rational reasons: lack of authentication, no spam filtering, forced synchronous communication. But also just a visceral fear. 2017-11-08Granddad died today Granddad died. The unspoken practice of death-by-dehydration in the NHS. The Liverpool Care Pathway. Assisted dying in the UK. The importance of planning in end-of-life care. 2017-05-19How do classes work in JavaScript? JavaScript class syntax is just syntactic sugar over the traditional prototype-based inheritance. Deconstructing a simple class reveals the underlying constructor functions, prototype assignment, and use of super to call the superclass’s constructor. 2020-11-03

More by Jim

What does the dot do in JavaScript? foo.bar, foo.bar(), or foo.bar = baz - what do they mean? A deep dive into prototypical inheritance and getters/setters. 2020-11-01Smear phishing: a new Android vulnerability Trick Android to display an SMS as coming from any contact. Convincing phishing vuln, but still unpatched. 2020-08-06A probabilistic pub quiz for nerds A “true or false” quiz where you respond with your confidence level, and the optimal strategy is to report your true belief. 2020-04-26Time is running out to catch COVID-19 Simulation shows it’s rational to deliberately infect yourself with COVID-19 early on to get treatment, but after healthcare capacity is exceeded, it’s better to avoid infection. Includes interactive parameters and visualizations. 2020-03-14The inception bar: a new phishing method A new phishing technique that displays a fake URL bar in Chrome for mobile. A key innovation is the “scroll jail” that traps the user in a fake browser. 2019-04-27The hacker hype cycle I got started with simple web development, but because enamored with increasingly esoteric programming concepts, leading to a “trough of hipster technologies” before returning to more productive work. 2019-03-23Project C-43: the lost origins of asymmetric crypto Bob invents asymmetric cryptography by playing loud white noise to obscure Alice’s message, which he can cancel out but an eavesdropper cannot. This idea, published in 1944 by Walter Koenig Jr., is the forgotten origin of asymmetric crypto. 2019-02-16How Hacker News stays interesting Hacker News buried my post on conspiracy theories in my family due to overheated discussion, not censorship. Moderation keeps the site focused on interesting technical content. 2019-01-26My parents are Flat-Earthers For decades, my parents have been working up to Flat-Earther beliefs. From Egyptology to Jehovah’s Witnesses to theories that human built the Moon billions of years in the future. Surprisingly, it doesn’t affect their successful lives very much. For me, it’s a fun family pastime. 2019-01-20The dots do matter: how to scam a Gmail user Gmail’s “dots don’t matter” feature lets scammers create an account on, say, Netflix, with your email address but different dots. Results in convincing phishing emails. 2018-04-07The sorry state of OpenSSL usability OpenSSL’s inadequate documentation, confusing key formats, and deprecated interfaces make it difficult to use, despite its importance. 2017-12-02I hate telephones I hate telephones. Some rational reasons: lack of authentication, no spam filtering, forced synchronous communication. But also just a visceral fear. 2017-11-08The Three Ts of Time, Thought and Typing: measuring cost on the web Businesses often tout “free” services, but the real costs come in terms of time, thought, and typing required from users. Reducing these “Three Ts” is key to improving sign-up flows and increasing conversions. 2017-10-26Granddad died today Granddad died. The unspoken practice of death-by-dehydration in the NHS. The Liverpool Care Pathway. Assisted dying in the UK. The importance of planning in end-of-life care. 2017-05-19How do I call a program in C, setting up standard pipes? A C function to create a new process, set up its standard input/output/error pipes, and return a struct containing the process ID and pipe file descriptors. 2017-02-17Your syntax highlighter is wrong Syntax highlighters make value judgments about code. Most highlighters judge that comments are cruft, and try to hide them. Most diff viewers judge that code deletions are bad. 2014-05-11Want to build a fantastic product using LLMs? I work at Granola where we're building the future IDE for knowledge work. Come and work with us! Read more or get in touch!

This page copyright James Fisher 2020. Content is not associated with my employer. Found an error? Edit this page.

Jim FisherCVSpeakingBlogrollRSS Granola

Tag » What Does Do In Javascript