🤞🤞Double Cross

The Ancient Scroll of Forgotten Functions

TL;DR: A monolithic IIFE doing prototype hacking, browser detection, and module loading all at once.

#code-smells#javascript#complexity#maintainability

The Code

javascript
1(function(scope){
2"use strict";
3
4var inBrowser = (typeof scope["HTMLElement"] != "undefined");
5
6// Global type registry - no namespacing, no conflict detection
7var _t_map = {};
8
9var inBrowserProtoFlip = (!inBrowser) ?
10  function(realCtor, parent, rp, props) { return realCtor; } :
11  function(realCtor, parent, rp, props) {
12    // If we're in a browser, we want to support "subclassing" HTML elements.
13    // This needs some magic and we rely on a wrapped constructor hack
14    if (parent && parent.prototype instanceof scope.HTMLElement) {
15      var intermediateCtor = realCtor;
16      var tn = getTagName(rp);
17      var upgrade = function(el) {
18        el.__proto__ = rp;  // Direct prototype manipulation!
19        intermediateCtor.apply(el, arguments);
20        if (rp["created"]) { el.created(); }
21        if (rp["decorate"]) { el.decorate(); }
22        return el;
23      };
24      c.extend(rp, { upgrade: upgrade });
25
26      realCtor = function() {
27        // We hack the constructor to always return an element
28        return upgrade(scope.document.createElement(tn));
29      }
30      realCtor.prototype = rp;
31      c.extend(realCtor, { ctor: intermediateCtor }); // HACK!!!
32    }
33    return realCtor
34  };
35
36c.extend = function(obj, props) {
37  c.own(props, function(x) {
38    var pd = Object.getOwnPropertyDescriptor(props, x);
39    if ( (typeof pd["get"] == "function") ||
40         (typeof pd["set"] == "function") ) {
41      Object.defineProperty(obj, x, pd);
42    } else if (typeof pd["value"] == "function" ||x.charAt(0) === "_") {
43      pd.writable = true;
44      pd.configurable = true;
45      pd.enumerable = false;
46      Object.defineProperty(obj, x, pd);
47    } else {
48        try {
49          obj[x] = props[x];
50        } catch(e) {
51          // TODO(slightlyoff): squelch, e.g. for tagName?
52        }
53    }
54  });
55  return obj;
56};
57
58// Module system detection - needs to work everywhere
59if (typeof define === 'function' && define.amd) {
60  define(c);
61} else if (typeof module === 'object' && module.exports) {
62  module.exports = c;
63} else {
64  scope.c = c;
65}
66
67})(this);
68

(Excerpt from larger file - showing relevant section only)

The Prayer 🤞🤞

🤞🤞 Please let this mystical incantation of prototype manipulation and DOM wizardry continue to work across browsers, despite the fact that we're literally hacking constructors and praying that __proto__ exists. May the JavaScript gods forgive us for this IIFE that does approximately seventeen different things and somehow still functions.

The Reality Check

This code is a masterclass in "it works on my machine" engineering. The browser detection logic, prototype chain manipulation, and constructor hacking create a fragile house of cards that will topple the moment a new browser version changes how HTMLElement inheritance works. The inBrowserProtoFlip function is particularly terrifying - it's dynamically rewriting constructors based on runtime conditions, which means debugging becomes a nightmare when things inevitably break.

The module system detection at the bottom suggests this code needs to work everywhere, but the HTMLElement subclassing hack only works in browsers that support __proto__. This creates inconsistent behavior across environments where the same code path produces different object structures. When a junior developer tries to extend this system, they'll spend days figuring out why their objects work differently in Node.js versus Chrome.

The global _t_map object for type serialization creates hidden dependencies throughout the codebase. If two modules register the same type key, the last one wins, silently breaking the first. There's no error handling for type conflicts, no documentation about the registration system, and no way to debug why deserialization suddenly returns the wrong object type.

The Fix

Start by breaking this monolith into focused modules with single responsibilities. Create separate files for inheritance utilities, DOM element handling, and serialization:

javascript
1// inheritance.js
2export function createClass(options) {
3  const { parent, initialize, ...methods } = options;
4  const Constructor = initialize || function() {};
5  
6  if (parent) {
7    Constructor.prototype = Object.create(parent.prototype);
8    Constructor.prototype.constructor = Constructor;
9    Constructor.super = parent;
10  }
11  
12  Object.assign(Constructor.prototype, methods);
13  return Constructor;
14}
15

Replace the browser detection hack with modern web components or a proper custom element registration system. If you must support legacy browsers, use a well-tested polyfill instead of rolling your own prototype manipulation:

javascript
1// custom-elements.js
2export function defineCustomElement(tagName, baseClass) {
3  if (customElements && customElements.define) {
4    customElements.define(tagName, baseClass);
5  } else {
6    // Use a proper polyfill, not homegrown hacks
7    return polyfillCustomElement(tagName, baseClass);
8  }
9}
10

For the serialization system, implement a proper registry with error handling and namespace support. Make type registration explicit and validate for conflicts:

javascript
1// serialization.js
2class TypeRegistry {
3  constructor() {
4    this.types = new Map();
5  }
6  
7  register(typeName, constructor, namespace = 'default') {
8    const key = `${namespace}:${typeName}`;
9    if (this.types.has(key)) {
10      throw new Error(`Type ${key} already registered`);
11    }
12    this.types.set(key, constructor);
13  }
14  
15  deserialize(data) {
16    const { _t, _ns = 'default', ...props } = data;
17    const constructor = this.types.get(`${_ns}:${_t}`);
18    return constructor ? constructor.fromJSON(props) : data;
19  }
20}
21

Lesson Learned

When your utility library needs extensive comments explaining browser hacks and prototype manipulation, it's time to split it into focused modules with clear responsibilities.