
Product
Socket for Jira Is Now Available
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.
pop-observe
Advanced tools
Property, range, map, and set content change observers for arrays, objects, and other instances.
This JavaScript package, suitable for browsers and Node.js, provides a system for synchronously observing content changes to arrays, objects, and other instances. These observers have a common, composable style, expose their internal state for debugging, and reuse state tracking objects to reduce garbage collection.
npm install --save pop-observe@2
Observing the length of an array.
var array = [];
var observer = O.observePropertyChange(array, "length", function (length) {
expect(length).toBe(1);
});
array.push(10);
observer.cancel();
Observing a property of an ordinary object.
var object = {weight: 10};
var observer = O.observePropertyChange(object, "weight", function (weight) {
expect(weight).toBe(20);
});
object.weight = 20;
observer.cancel();
Observing values at indexes.
var array = [];
var change;
var handler = {
handlePropertyChange: function (plus, minus, index, object) {
change = {
plus: plus,
minus: minus,
index: index,
object: object
};
}
};
var observer0 = O.observePropertyChange(array, 0, handler);
var observer1 = O.observePropertyChange(array, 1, handler);
var observer2 = O.observePropertyChange(array, 2, handler);
array.set(0, 10);
expect(change).toEqual({plus: 10, minus: undefined, index: 0, object: array});
array.set(0, 20);
expect(change).toEqual({plus: 20, minus: 10, index: 0, object: array});
array.set(1, 20);
expect(change).toEqual({plus: 20, minus: undefined, index: 1, object: array});
Mirroring arrays.
var O = require("pop-observe");
var swap = require("pop-swap");
var array = [];
var mirror = [];
var observer = O.observeRangeChange(array, function (plus, minus, index) {
swap(mirror, index, minus.length, plus);
});
array.push(1, 2, 3);
array.shift();
array.pop();
expect(mirror).toEqual([2]);
observer.cancel();
Tracking an array with a plain object.
var O = require("pop-observe");
var array = [];
var object = {};
var observer = O.observeMapChange(array, function (plus, minus, index, type) {
if (type === "delete") {
delete object[index];
} else { // type === "create" || type === "update"
object[index] = plus;
}
});
array.push(1, 2, 3);
expect(object).toEqual({0: 1, 1: 2, 2: 3});
array.splice(1, 1);
expect(object).toEqual({0: 1, 1: 3});
Observing a property of a property. Note that the cancel method gets rid of observers on a.b and b.c. Note that the b.c observer gets canceled every time b changes.
var O = require("pop-observe");
var a = {b: {c: 10}};
var value;
var observer = O.observePropertyChange(a, "b", function (b) {
value = b.c;
return O.observePropertyChange(b, "c", function (c) {
value = c;
});
});
a.b = {c: 20};
expect(value).toBe(20);
a.b.c = 30;
expect(value).toBe(30);
observer.cancel();
Arbitrary constructors can mix in or inherit the ObservableObject type to support the observable interface directly and do not need to provide any further support.
var inherits = require("util").inherits;
var ObservableObject = require("pop-observe/observable-object");
function Custom() {}
inherits(Custom, ObservableObject);
Arbitrary constructors can mix in or inherit the ObservableRangeChange type and must explicitly dispatch change notifications when range change observers are active.
var inherits = require("util").inherits;
var ObservableRange = require("pop-observe/observable-range");
function Custom() {}
inherits(Custom, ObservableRange);
Customer.prototype.unshift = function unshift(value) {
if (this.dispatchesRangeChanges) {
this.dispatchRangeWillChange([value], [], 0);
}
// actual work
if (this.dispatchesRangeChanges) {
this.dispatchRangeChange([value], [], 0);
}
};
This library does not provide any map implementations but provides the ObservableMap for any to inherit or mix in.
var inherits = require("util").inherits;
var ObservableMap = require("pop-observe/observable-map");
function Custom() {}
inherits(Custom, ObservableMap);
Customer.prototype.delete = function delete(key) {
var old = this.get(key);
if (!old) {
return;
}
if (this.dispatchesMapChanges) {
this.dispatchMapWillChange("delete", key, undefined, old);
}
// actual work
if (this.dispatchesMapChanges) {
this.dispatchMapChange("delete", key, undefined, old);
}
};
All of thse can be mixed by copying the properties from their prototypes.
var ObservableObject = require("pop-observe/observable-object");
// Your favorite property copying idiom here
var owns = Object.prototype.hasOwnProperty;
for (var name in ObservableObject.prototype) {
if (owns.call(ObservableObject.prototype, name)) {
Customer.prototype[name] = ObservableObject.prototype[name];
}
}
Each type of observer provides before and after methods for observation and
manual dispatch.
For properties, manual dispatch is necessary when a property is hidden behind a
getter and a setter if the value as returned by the getter changes without the
setter ever being invoked.
Arrays require manual dispatch only if the value at a given index changes
without invoking an array mutation method.
For this reason, observable arrays have a set(index, value) method.
All ranged and map collections must implement manual dispatch when their
dispatchesRangeChanges or dispatchesMapChanges properties are true.
Object property change observers
observePropertyChange(object, handler, note, capture) -> Observer
observePropertyWillChange(object, handler, note) -> Observer
dispatchPropertyChange(object, name, plus, minus, capture)
dispatchPropertyWillChange(object, name, plus, minus)
makePropertyObservable(object, name)
preventPropertyObserver(object, name)
Range change observers
Map change observers
Observer objects in general
Handlers may be raw functions, or objects with one or more handler methods.
Observers for different kinds of changes and before and after changes call
different methods of the handler object based on their availability at the time
that the observer is created.
For example, observePropertyWillChange(array, "length", handler) will create a
property observer that will delegate to the
handler.handleLengthPropertyWillChange(plus, minus, key, object) method, or
just that generic handler.handlePropertyWillChange(plus, minus, key, object)
method if the specific method does not exist.
Range changes do not operate on a given property name, but the
observeRangeChange(handler, name, note, capture) method allows you to give the
range change a name, for example, the name of the array observed on a given
object.
var handler = {
handleValuesRangeChange: function (plus, minus, index, object) {
// ...
}
};
var observer = repetition.values.observeRangeChange(handler, "values");
// ...
repetition.values.push(10);
// ...
observer.cancel();
Likewise, observeMapChange(handler, name, note, capture) accepts a name for a
specific handler method.
Observers are re-usable objects that capture the state of the observer.
Most importantly, they provide the cancel method, which disables the observer
and returns it to a free list for the observer methods to re-use.
They are suitable for run-time inspection of the state of the observer.
They also carry an informational note property, if the caller of the observe
method provided one.
This is intended for use by third parties to provide additional debugging
information about an observer, for example, where the observer came from.
The pop-observe/observable-object, pop-observe/observable-range, and
pop-observe/observable-map modules export mixable or prototypically
inheritable constructors.
Objects that inherit the observable interface must then dispatch change and will
change notifications if they are being observed, in all of their methods that
change their content.
ObservableObject.observePropertyChange(object, handler, note, capture)
ObservableObject.observePropertyWillChange(object, handler, note)
ObservableObject.dispatchPropertyChange(object, name, plus, minus, capture)
ObservableObject.dispatchPropertyWillChange(object, name, plus, minus)
ObservableObject.getPropertyChangeObservers(object, name, capture)
ObservableObject.getPropertyWillChangeObservers(object, name)
ObservableObject.makePropertyObservable(object, name)
ObservableObject.preventPropertyObserver(object, name)
ObservableObject.prototype.observePropertyChange(handler, note, capture)
ObservableObject.prototype.observePropertyWillChange(handler, note)
ObservableObject.prototype.dispatchPropertyChange(name, plus, minus, capture)
ObservableObject.prototype.dispatchPropertyWillChange(name, plus, minus)
ObservableObject.prototype.getPropertyChangeObservers(name, capture)
ObservableObject.prototype.getPropertyWillChangeObservers(name)
ObservableObject.prototype.makePropertyObservable(name)
ObservableObject.prototype.preventPropertyObserver(name)
ObservableRange.prototype.observeRangeChange(handler, name, note, capture)
ObservableRange.prototype.observeRangeWillChange(handler, name, note)
ObservableRange.prototype.dispatchRangeChange(handler, name, note, capture)
ObservableRange.prototype.dispatchRangeWillChange(handler, name, note)
ObservableRange.prototype.makeRangeChangesObservable()
ObservableMap.prototype.observeMapChange(handler, name, note, capture)
ObservableMap.prototype.observeMapWillChange(handler, name, note)
ObservableMap.prototype.dispatchMapChange(type, key, plus, minus, capture)
ObservableMap.prototype.dispatchMapWillChange(type, key, plus, minus)
ObservableMap.prototype.makeMapChangesObservable()
Copyright (c) 2015 Motorola Mobility, Montage Studio, Kristopher Michael Kowal, and contributors. All rights reserved. BSD 3-Clause license.
FAQs
Property, range, map, and set content change observers for arrays, objects, and other instances.
The npm package pop-observe receives a total of 7,003 weekly downloads. As such, pop-observe popularity was classified as popular.
We found that pop-observe demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Product
Socket for Jira lets teams turn alerts into Jira tickets with manual creation, automated ticketing rules, and two-way sync.

Company News
Socket won two 2026 Reppy Awards from RepVue, ranking in the top 5% of all sales orgs. AE Alexandra Lister shares what it's like to grow a sales career here.

Security News
NIST will stop enriching most CVEs under a new risk-based model, narrowing the NVD's scope as vulnerability submissions continue to surge.