npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@selfage/observable_js

v1.0.4

Published

Observe objects through ES6 Proxy.

Downloads

32

Readme

@selfage/observable_js

Observe objects through ES6 Proxy.

Function sigatures

/**
 * @param {object} obj - Any object except null.
 * @returns {object} observable object
 **/
function toObservable(obj) {}
/**
 * @param {object} obj - Any object except null.
 * @returns {boolean} true if `obj` is observable. See caveats for when it's not
 *   accurate.
 **/
function isObservable(obj) {}

/**
 * @callback Handler
 * @param {string} prop - The name of the changed property.
 * @param {any} newValue - The new value of the changed property.
 * @param {any} oldValue - The supposed old value of the changed proerpty. When
 *   propagated, it's the same as `newValue`.
 * @param {object} obj - The object to which the property belongs. See caveats
 *   if you want to use it.
 * @returns {void}
 */
/**
 * Available on an observable object.
 * @param {Handler} handler - To be be invoked when there is a change on any
 *   properties belonged to the observable object.
 * @returns {void} after calling all added handlers and don't wait for async
 *   operations.
 **/
function addPropertyChangeHandler(handler) {}
/**
 * Available on an observable object. Removing takes O(n) time where n is the
 * number of all handlers added to the observable object.
 * @param {Handler} handler - Must have the same reference to a handler added
 *   above.
 * @returns {void}
 **/
function removePropertyChangeHandler(handler) {}

Usage

Flat object

const {toObservable, isObservable} = require('@selfage/observable_js');

const ob = toObservable({num: 100, str: 'lol', bul: false});
console.log(isObservable(ob));
// Print true

function logChange(prop, newValue, oldValue) {
  console.log(`${prop} is changed from ${oldValue} to ${newValue}.`);
}
ob.addPropertyChangeHandler(logChange);
ob.num = 200;
// Print "num is changed from 100 to 200."
ob.num2 = 300;
// Print "num2 is changed from undefined to 300."
ob.removePropertyChangeHandler(logChange);
ob.num = 150;
// Nothing to print.

Array

Array is just an object, except that its length property can be updated when push() or pop(). Note that its index is treated as a string.

const {toObservable, isObservable} = require('@selfage/observable_js');

const ob = toObservable([11,22,33,44,55]);
console.log(isObservable(ob));
// Print true

function logChange(prop, newValue, oldValue) {
  console.log(
    `${prop}[${typeof prop}] is changed from ${oldValue} to ${newValue}.`
  );
}
ob.addPropertyChangeHandler(logChange);
ob[2] = 100;
// Print "2[string] is changed from 33 to 100.";
ob.push(66);
// Print "5[string] is changed from undefined to 66."
// Oddly it might not print length.
ob.pop();
// Print "5[string] is changed from 66 to undefined."
// Print "length[string] is changed from 6 to 5".

Nested object

All nested objects will be observable. Changes in nested objects are propagated/bubbled up until the top-level object. However, newValue and oldValue refer to the same nested object after propagating/bubbling up.

const {toObservable, isObservable} = require('@selfage/observable_js');

const ob = toObservable({
  num: 100,
  nobj: {
    value: 200
  }
});
console.log(isObservable(ob.nobj));
// Print "true"

function logChange(prop, newValue, oldValue) {
  console.log(
    `${prop} is changed from ${JSON.stringify(oldValue)} to ` +
    `${JSON.stringify(newValue)}.`
  );
}
ob.addPropertyChangeHandler(logChange);
ob.nobj.addPropertyChangeHandler(logChange);
ob.nobj.value = 300;
// Print "value is changed from 200 to 300."
// Print "nobj is changed from {"value":300} to {"value":300}."

Add nested object

Add/Assign a nested object will automatically convert it to be observable.

const {toObservable, isObservable} = require('@selfage/observable_js');

const ob = toObservable({num: 100});
ob.nobj = {value: 200};
console.log(isObservable(ob.nobj));
// Print "true"

Assigning an observable nested object is safe.

const {toObservable, isObservable} = require('@selfage/observable_js');

const ob = toObservable({num: 100});
ob.nobj = toObservable({value: 200});
console.log(isObservable(ob.nobj));
// Print "true"

Caveats

delete vs null vs undefined

const {toObservable, isObservable} = require('@selfage/observable_js');

// toObservable(null) results in an error. Never use null.

const ob = toObservable({num: 100});

function logChange(prop, newValue, oldValue) {
  console.log(`${prop} is changed from ${oldValue} to ${newValue}.`);
}
ob.addPropertyChangeHandler(logChange);
delete ob.num;
// Print "num is changed from 100 to undefined".
ob.num = undefined;
// Nothing to print, since ob.num === undefined already.
// If we set `ob.num = undefined` first and `delete ob.num`, logChange() will
// also not be invoked for `delete ob.num`.
delete ob.num;
// Still nothing to print.
// You should decide to delete or set to undefined, depending on how you woul
// deal with `'num' in ob` or `Object.keys()`.

Is object passed to handlers observable?

const {toObservable, isObservable} = require('@selfage/observable_js');

const ob = toObservable({
  num: 100,
  nobj: {
    value: 200
  }
});

let capturedTopLevel = {obj: undefined, nestedObj: undefined};
function captureTopLevel(prop, newValue, oldValue, obj) {
  capturedTopLevel.obj = obj;
  capturedTopLevel.nestedObj = newValue;
}
let capturedSecondLevel = {obj: undefined};
function captureSecondLevel(prop, newValue, oldValue, obj) {
  capturedTopLevel.obj = obj;
}
ob.addPropertyChangeHandler(captureTopLevel);
ob.nobj.addPropertyChangeHandler(captureSecondLevel);
ob.nobj.value = 300;
ob.removePropertyChangeHandler(captureTopLevel);
ob.nobj.removePropertyChangeHandler(captureSecondLevel);

let topLevelCount = 0;
function countTopLevelChange() {
  topLevelCount++;
}
let secondLevelCount = 0;
function countSecondLevelChange() {
  secondLevelCount++;
}
ob.addPropertyChangeHandler(countTopLevelChange);
ob.nobj.addPropertyChangeHandler(countSecondLevelChange);
ob.nobj.value = 400;
// topLevelCount === 1 && secondLevelCount === 1
capturedSecondLevel.obj.value = 500;
// No change: topLevelCount === 1 && secondLevelCount === 1
capturedTopLevel.obj.num = 0;
// No change: topLevelCount === 1 && secondLevelCount === 1
capturedTopLevel.nestedObj.value = 600;
// topLevelCount === 2 && secondLevelCount === 2
// Conclusion: The fourth parameter `obj` is not trully observable, although
// `isObservable(capturedTopLevel.obj)` and
// `isObservable(capturedSecondLevel.obj)` are both true;

More cases

You can find almost all use cases in observable_test.js and observable_test2.js.