Exploring the Enigmatic:

Navigating JavaScript's Uncharted Realms

 Luca Del Puppo 

I want to be honest

Today, I'll talk about something that common developers don't use directly in the daily.

But probably the frameworks use these things for them, under the hood.

Luca Del Puppo

  • Senior Software Developer
  • JavaScript enthusiast
  • TypeScript lover
  • “Youtuber”
  • “Writer”

Love sport: running, hiking

Love animals

const button = 
      document.createElement('button');

button.clicked = false;

button.addEventListener('click', () => {
  button.clicked = true
});

writable

enumerable

configurable

Object Property Descriptors

Syntax

const person = {};
Object.defineProperty(person, 'name',
    {
        value: 'Alice',
        writable: false,
        enumerable: true,
        configurable: false
    }
);

Syntax

const animal = {};
Object.defineProperties(animal, {
    name: {
        value: 'Dog',
        writable: true
    },
    sound: {
        value: 'Woof',
        enumerable: false
    }
});

Syntax

const myObj = { age: 30 };
const descriptor =
      Object.getOwnPropertyDescriptor(myObj, 'age');

console.log(descriptor);
/* Output: 
  {
    value: 30, 
    writable: true,
    enumerable: true,
    configurable: true
  }
*/

Common usage

Objects Property Descriptors are used when we want to define every single behaviour of the properties.

By default every attribute is set to true.

Characteristics

  • permit to define every attribute of every property
  • allow to configure the property once and than it's "immutable"
  • allow to configure a property read-only
  • allow to hide a property from the enumaration
const button = 
      document.createElement('button');

Object.definedProperty(button, 'clicked', {
  value: false,
  writable: true,
  enumerable: false,
  configurable: false
})

button.addEventListener('click', () => {
  button.clicked = true
});

Symbols

Syntax

const propSymbol = Symbol("propName");
const propSymbol2 = Symbol("propName");

propSymbol === propSymbol2 // false

Don't do this


const propSymbol = new Symbol("propName");

Common usage

Symbols are often used as property keys in objects to ensure uniqueness and prevent naming collisions.

Example

const nameSymbol = Symbol("name");
const surnameSymbol = Symbol("surname");

const person = {
  name: 'John',
  surname: 'Doe',
  [nameSymbol]: "Jane",
  [surnameSymbol]: "Blue"
}

Characteristics

  • unique
  • live for the entire lifecycle of your application
  • not enumerable (hidden from for...in and Object.keys)
  • use Object.getOwnPropertySymbols() to get them

What about sharing Symbols

Global Symbol Registry

// my-module-1.js
export const propSymbolShare = Symbol.for("propName");

// my-module-2.js
export const propSymbolShare = Symbol.for("propName");

// index.js
import { propSymbolShare as propSymbolShareM1 } from './my-module.js'
import { propSymbolShare as propSymbolShareM2 } from './my-module-2.js'

console.log(propSymbolShareM1 === propSymbolShareM2); // true

Well-Known Symbols

Well-known symbols are predefined global symbols that represent common behaviours in JavaScript, such as Symbol.iterator, Symbol.asyncIterator, Symbol.toStringTag, and Symbol.species.

Example

function Range(start, end) {
  this.start = start;
  this.end = end;

  this[Symbol.iterator] = function*() {
    let current = this.start;
    const last = this.end;

    while (current <= last) {
      yield current++;
    }
  }

  return this;
}

for (let num of new Range(1, 10)) {
  console.log(num);
}
const ClickedSymbol = Symbol('clicked')

const button = 
      document.createElement('button');

button[ClickedSymbol] = false

button.addEventListener('click', () => {
  button[ClickedSymbol] = true
});

WeakMaps

Syntax

const weakmap = new WeakMap();
const key = { name: "John Doe" };
weakmap.set(key, "Some value");

const value = weakmap.get(key); // "Some value"

weakmap.delete(key); // true
weakmap.has(key); // false

Common usage

WeakMap offers several practical applications in JavaScript:

  1. Privacy and Encapsulation
  2. Memory Management
  3. Caching

Characteristics

  • a Map but with only objects as keys
  • manage memory management

Limitations

  • Keys must be objects

  • No built-in iteration methods: such as forEach or entries.

  • No clear method

const ClickedWeakMap = new WeakMap()

const button = 
      document.createElement('button');

ClickedWeakMap.set(button, false)

button.addEventListener('click', () => {
  ClickedWeakMap.set(button, true)
});

WeakSets

Syntax

const weakSet = new WeakSet();

const obj1 = {};

weakSet.add(obj1);

console.log(weakSet.has(obj1)); // true
weakSet.delete(obj1);
console.log(weakSet.has(obj1)); // false

Common usage

WeakSet offers several practical applications in JavaScript:

  1. Managing DOM Elements
  2. Private Data Storage

Characteristics

  • a Set but with only objects
  • manage memory management

Limitations

  • Accepts only objects

  • No built-in iteration methods, such as forEach etc. etc.

  • No clear method

const ClickedWeakSet = new WeakSet()

const button = 
      document.createElement('button');

button.addEventListener('click', () => {
  ClickedWeakSet.add(button)
});

Proxies

Syntax - get

const target = { age: 31 };
const handler = {
  get(_target, prop) {
    if (prop === "birthYear") {
       return new Date().getFullYear() - _target['age'];
    }
    return _target[prop];
  }
};

const proxy = new Proxy(target, handler);
console.log(proxy.birthYear); // 1992

Syntax - set

const target = { age: 25 };
const handler = {
  set(_target, prop, value) {
    if (prop === "age" 
        && (typeof value !== "number" || value < 0 || value > 150))
      throw new TypeError("Invalid age value");

    _target[prop] = value;
    return true;
  },
};

const proxy = new Proxy(target, handler);
proxy.age = 30; // Works fine
proxy.age = "thirty"; // Throws TypeError: "Invalid age value"

Others traps

  1. has
  2. apply
  3. constructor
  4. deleteProperty

Common usage

  1. Validation and Constraints
  2. Logging and Profiling
  3. Reactivity
const handlers = {
  set(t, p, v) {
    if (p === 'clicked')
      throw new Error('You cannot change this')
    t[p] = v
  }
}
const button = document.createElement('button')
button.addEventListener('click', () => {
  button.clicked = true
});

const wrappedButton = new Proxy(button, handlers)

Reflect

Syntax

const numbers = [1, 2, 3];
const sum = (a, b, c) => a + b + c;

// Using Reflect.apply()
const result = Reflect.apply(sum, null, numbers);

console.log(result); // Output: 6

Syntax

const obj = { x: 42, y: 'hello' };

// Using Reflect.get()
const value = Reflect.get(obj, 'x');

console.log(value); // Output: 42

Syntax

const obj = { x: 42 };
const newObj = Object.create(obj);

// Using Reflect.set()
Reflect.set(newObj, 'x', 13);

console.log(newObj.x); // Output: 13

Syntax

const obj = {};

// Using Reflect.defineProperty()
Reflect.defineProperty(obj, 'x', {
  value: 42,
  writable: false
});

console.log(obj.x); // Output: 42

Syntax

const obj = { x: 42, [Symbol('key')]: 'symbolValue' };

// Using Reflect.ownKeys()
const keys = Reflect.ownKeys(obj);

console.log(keys); // Output: ['x', Symbol(key)]

Why should I dive into Reflect when there are other ways to manipulate and inspect JavaScript objects?

Some points

  • Functional Paradigm
  • Enhanced Error Handling
  • Proxy Integration
  • Consistency and Predictability

DEMO

Conclusion

Conclusion

  1. JavaScript is awesome 🚀
  2. Different ways exist to do the same stuff but with different trade-offs and results.
  3. Pay attention to what the other can do with your objects
  4. JavaScript gives us the Power. But with great power comes great responsibility.
  5. You use all these features daily, so be confident about what libraries do for us. It's important

Slides

Code

YouTube Videos & Blog Posts

Javascript you don't know

Luca Del Puppo

@puppo92

Luca Del Puppo

Puppo_92

@puppo

We are hiring

Thank you!_