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

observe-js

v0.5.7

Published

observe-js is a library for observing changes on JavaScript objects/arrays

Downloads

7,964

Readme

Analytics

Learn the tech

Why observe-js?

observe-js is a library for observing changes in JavaScript data. It exposes a high-level API and uses Object.observe if available, and otherwise performs dirty-checking. observe-js requires ECMAScript 5.

Observable

observe-js implements a set of observers (PathObserver, ArrayObserver, ObjectObserver, CompoundObserver, ObserverTransform) which all implement the Observable interface:

{
  // Begins observation. Value changes will be reported by invoking |changeFn| with
  // |opt_receiver| as the target, if provided. Returns the initial value of the observation.
  open: function(changeFn, opt_receiver) {},

  // Report any changes now (does nothing if there are no changes to report).
  deliver: function() {},

  // If there are changes to report, ignore them. Returns the current value of the observation.
  discardChanges: function() {},

  // Ends observation. Frees resources and drops references to observed objects.
  close: function() {}
}

PathObserver

PathObserver observes a "value-at-a-path" from a given object:

var obj = { foo: { bar: 'baz' } };
var defaultValue = 42;
var observer = new PathObserver(obj, 'foo.bar', defaultValue);
observer.open(function(newValue, oldValue) {
  // respond to obj.foo.bar having changed value.
});

PathObserver will report a change whenever the value obtained by the corresponding path expression (e.g. obj.foo.bar) would return a different value.

PathObserver also exposes a setValue method which attempts to update the underlying value. Setting the value does not affect notification state (in other words, a caller sets the value but does not discardChanges, the changeFn will be notified of the change).

observer.setValue('boo');
assert(obj.foo.bar == 'boo');

Notes:

  • If the path is ever unreachable, the value is considered to be undefined (unless you pass an overriding defaultValue to new PathObserver(...) as shown in the above example).
  • If the path is empty (e.g. ''), it is said to be the empty path and its value is its root object.
  • PathObservation respects values on the prototype chain

ArrayObserver

ArrayObserver observes the index-positions of an Array and reports changes as the minimal set of "splices" which would have had the same effect.

var arr = [0, 1, 2, 4];
var observer = new ArrayObserver(arr);
observer.open(function(splices) {
  // respond to changes to the elements of arr.
  splices.forEach(function(splice) {
    splice.index; // the index position that the change occurred.
    splice.removed; // an array of values representing the sequence of removed elements
    splice.addedCount; // the number of elements which were inserted.
  });
});

ArrayObserver also exposes a utility function: applySplices. The purpose of applySplices is to transform a copy of an old state of an array into a copy of its current state, given the current state and the splices reported from the ArrayObserver.

AraryObserver.applySplices = function(previous, current, splices) { }

ObjectObserver

ObjectObserver observes the set of own-properties of an object and their values.

var myObj = { id: 1, foo: 'bar' };
var observer = new ObjectObserver(myObj);
observer.open(function(added, removed, changed, getOldValueFn) {
  // respond to changes to the obj.
  Object.keys(added).forEach(function(property) {
    property; // a property which has been been added to obj
    added[property]; // its value
  });
  Object.keys(removed).forEach(function(property) {
    property; // a property which has been been removed from obj
    getOldValueFn(property); // its old value
  });
  Object.keys(changed).forEach(function(property) {
    property; // a property on obj which has changed value.
    changed[property]; // its value
    getOldValueFn(property); // its old value
  });
});

CompoundObserver

CompoundObserver allows simultaneous observation of multiple paths and/or Observables. It reports any and all changes in to the provided changeFn callback.

var obj = {
  a: 1,
  b: 2,
};

var otherObj = { c: 3 };

var observer = new CompoundObserver();
observer.addPath(obj, 'a');
observer.addObserver(new PathObserver(obj, 'b'));
observer.addPath(otherObj, 'c');
var logTemplate = 'The %sth value before & after:';
observer.open(function(newValues, oldValues) {
  // Use for-in to iterate which values have changed.
  for (var i in oldValues) {
    console.log(logTemplate, i, oldValues[i], newValues[i]);
  }
});

ObserverTransform

ObserverTransform is used to dynamically transform observed value(s).

var obj = { value: 10 };
var observer = new PathObserver(obj, 'value');
function getValue(value) { return value * 2 };
function setValue(value) { return value / 2 };

var transform = new ObserverTransform(observer, getValue, setValue);

// returns 20.
transform.open(function(newValue, oldValue) {
  console.log('new: ' + newValue + ', old: ' + oldValue);
});

obj.value = 20;
transform.deliver(); // 'new: 40, old: 20'
transform.setValue(4); // obj.value === 2;

ObserverTransform can also be used to reduce a set of observed values to a single value:

var obj = { a: 1, b: 2, c: 3 };
var observer = new CompoundObserver();
observer.addPath(obj, 'a');
observer.addPath(obj, 'b');
observer.addPath(obj, 'c');
var transform = new ObserverTransform(observer, function(values) {
  var value = 0;
  for (var i = 0; i < values.length; i++)
    value += values[i]
  return value;
});

// returns 6.
transform.open(function(newValue, oldValue) {
  console.log('new: ' + newValue + ', old: ' + oldValue);
});

obj.a = 2;
obj.c = 10;
transform.deliver(); // 'new: 14, old: 6'

Path objects

A path is an ECMAScript expression consisting only of identifiers (myVal), member accesses (foo.bar) and key lookup with literal values (arr[0] obj['str-value'].bar.baz).

Path.get('foo.bar.baz') returns a Path object which represents the path. Path objects have the following API:

{
  // Returns the current value of the path from the provided object. If eval() is available,
  // a compiled getter will be used for better performance. Like PathObserver above, undefined
  // is returned unless you provide an overriding defaultValue.
  getValueFrom: function(obj, defaultValue) { },

  // Attempts to set the value of the path from the provided object. Returns true IFF the path
  // was reachable and set.
  setValueFrom: function(obj, newValue) { }
}

Path objects are interned (e.g. assert(Path.get('foo.bar.baz') === Path.get('foo.bar.baz'));) and are used internally to avoid excessive parsing of path strings. Observers which take path strings as arguments will also accept Path objects.

About delivery of changes

observe-js is intended for use in environments which implement Object.observe, but it supports use in environments which do not.

If Object.observe is present, and observers have changes to report, their callbacks will be invoked at the end of the current turn (microtask). In a browser environment, this is generally at the end of an event.

If Object.observe is absent, Platform.performMicrotaskCheckpoint() must be called to trigger delivery of changes. If Object.observe is implemented, Platform.performMicrotaskCheckpoint() has no effect.