Last active
August 24, 2023 20:55
-
-
Save dfkaye/8c1b6e202b0cc5ce4fae80b4b1faddfa to your computer and use it in GitHub Desktop.
Hazards of working everything out in the console (or a REPL) without strict mode.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 23 August 2023 | |
// Hazards of working everything out in the console (or a REPL). | |
// I really thought I'd found something interesting, a possible quirk in the | |
// `form.elements` collection, but it turned out to be anti-pattern that strict | |
// mode prevents. | |
// We relive some of the embarrassment for you now. | |
// First, we'll set up the DOM from the console. | |
var html = ` | |
<input id="a.b.c" name="x.y.z" value="* test *"/> | |
`; | |
document.body.replaceChildren(); | |
var form = document.createElement("form"); | |
// setHTML() is coming, more on that at MDN, | |
// https://developer.mozilla.org/en-US/docs/Web/API/Element/setHTML | |
typeof form.setHTML == 'function' | |
? form.setHTML(html) | |
: form.innerHTML = html; | |
document.body.appendChild(form); | |
var input = form.firstElementChild; | |
console.assert(input.id === "a.b.c", "should find input"); | |
// Now find the input by the form.elements collection. | |
// by ID | |
console.assert(form.elements["a.b.c"] === input, `"a.b.c"`); | |
// by name | |
console.assert(form.elements["x.y.z"] === input, `"x.y.z"`); | |
// Now the quirk I thought I'd found was that I could prefix the quoted search | |
// key with `name` or `id` or anything and the equals sign, `=`. | |
// The next two examples are run in IIFEs (i.e., "immediately invoked function | |
// expressions"). | |
// In the first example, we prefix `id=`, `name=`, and `any=` to the search | |
// keys, respectively. | |
(function () { | |
console.group("IIFE"); | |
// prefix ID | |
console.assert(form.elements[id="a.b.c"] === input, `id="a.b.c"`); | |
// prefix name | |
console.assert(form.elements[name="x.y.z"] === input, `name="x.y.z"`); | |
// prefix anything | |
console.assert(form.elements[any="x.y.z"] === input, `any="x.y.z"`); | |
console.groupEnd("IIFE") | |
})('IIFE'); | |
// It turns out that such prefixing also works in object and array indexes. | |
// In the next example, we prefix `id=` and `name=` to the search keys, | |
// respectively. | |
(function () { | |
console.group("O & A"); | |
var o = { "d.e.f": "value" } | |
console.assert(o[id="d.e.f"] === "value", `o[id="d.e.f"]`); | |
var a = ['a', 'b'] | |
console.assert(a[name=1] === 'b', `a[name=1]`); | |
console.groupEnd("O & A"); | |
})('IIFE'); | |
// On reflection, however, it turns out all we've really done is to create | |
// accidental global variables `id`, `name`, and `any`. | |
// They are "accidental" in the sense that they have been created without the | |
// intentional keywords "var", "let", or "const" preceding them. | |
// Even from inside a function, when a variable name is assigned a value without | |
// these keywords, the variable is set as a global entry at the top of the | |
// current scope (in this case, `self` or `window` or `globalThis`)... | |
console.assert(globalThis.id === id, "id is global"); | |
console.assert(self.name === name, "name is global"); | |
console.assert(window.any === any, "any is global"); | |
// `id` and `name` have been assigned from the object and array test... | |
console.assert(id === "d.e.f", `id assigned "d.e.f"`); | |
// ...and note that `name` is a string, not a number... | |
console.assert(name === "1", `name assigned 1`); | |
console.assert(typeof name == 'string', "expected name to be a string"); | |
// while `any` is still assigned from the first IIFE test... | |
console.assert(any === "x.y.z", `any assigned "x.y.z"`); | |
// So let's clean up `id` and `any` before explaining what went wrong... | |
delete id, delete any; | |
console.assert(!('id' in self), `should clear id`); | |
console.assert(!('any' in globalThis), `should clear any`); | |
// Why not delete `name`? | |
// We should not delete `name` from the console context because of another | |
// quirk where re-running this console script without re-loading the console | |
// will set name as a number, instead of its default "stringly" behavior. | |
// Instead, reassign it to the empty string (its default value in a newly | |
// constructed window). | |
name = ""; | |
// These variables were created from our function and console context because we | |
// did not run these examples in "strict" mode, which prevents accidental global | |
// assignment. | |
// ECMAScript modules run in strict mode by default, but older contexts - such | |
// as the browser console - do not. | |
// In those contexts we have to declare the `"use strict";`` pragma at the | |
// beginning of the scope we are interested in. | |
// If we do that in the following IIFE, there should be no failing assertions. | |
(function (error) { | |
"use strict"; | |
console.group("id"); | |
try { | |
console.assert(form.elements[id="a.b.c"] === input, `id="a.b.c"`); | |
} | |
catch (e) { | |
error = e; | |
} | |
finally { | |
console.assert( | |
error.constructor === ReferenceError | |
&& error.message.indexOf("id") != -1, | |
"should be ReferenceError for id" | |
); | |
} | |
console.groupEnd("id"); | |
console.group("any"); | |
try { | |
console.assert(form.elements[any="x.y.z"] === input, `any="x.y.z"`); | |
} | |
catch (e) { | |
error = e; | |
} | |
finally { | |
console.assert( | |
error.constructor === ReferenceError | |
&& error.message.indexOf("any") != -1, | |
"should be ReferenceError for any" | |
); | |
} | |
console.groupEnd("any"); | |
})(); | |
// Most of the time, console fragments don't fall into such trouble. | |
// But, if you're like me, and you like trying out potentially clever or chaotic | |
// ideas in the console, then the "use strict"; pragma is your friend. | |
// It will save you some - maybe hours of - confusion or embarrassment. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment