Oolong.js is a library for JavaScript full of object-related utilities. It's similar to Underscore.js, but it focuses strictly on functions dealing with objects. It's implementation emphasizes simplicity and good taste. For example, it always takes inherited properties into account leading to less surprises for users of your code.
Oolong.js grew out of my frustration with Underscore.js and Lodash.js and their inconsistent and sometimes outright ignorance of inherited properties. This leads to unnecessary complexity, arbitrary constraints and a leaky-implementation in your code or public APIs. This behavior is cancer propelled around by ignorance and misunderstandings between dictionaries and interfaces. Oolong.js is my first step at killing it.
npm install oolong
Oolong.js follows semantic versioning, so feel free to
depend on its major version with something like >= 1.0.0 < 2
(a.k.a ^1.0.0
).
For extended documentation on all functions, please see the Oolong.js API Documentation.
- .assign(target, source...)
- .assignOwn(target, source...)
- .clone(object)
- .cloneDeep(object)
- .create(prototype, [source...])
- .defaults(target, source...)
- .defineGetter(object, property, fn)
- .defineSetter(object, property, fn)
- .each(object, callback, [thisArg])
- .eachOwn(object, callback, [thisArg])
- .filter(object, callback, [thisArg])
- .forEach(object, callback, [thisArg])
- .forEachOwn(object, callback, [thisArg])
- .has(object, key)
- .hasOwn(object, key)
- .isEmpty(object)
- .isIn(object, key)
- .isInOwn(object, key)
- .isObject(object)
- .isOwnEmpty(object)
- .isPlainObject(object)
- .keys(object)
- .lookupGetter(object, property)
- .lookupSetter(object, property)
- .map(object, callback, [thisArg])
- .mapKeys(object, callback, [thisArg])
- .merge(target, source...)
- .object(keys, callback, [thisArg])
- .ownKeys(object)
- .pick(object, keys...)
- .pickDeep(object, keys...)
- .pluck(object, key)
- .property(key)
- .reject(object, callback, [thisArg])
- .setPrototypeOf(object, prototype)
- .values(object)
- .wrap(value, key)
Some JavaScript runtimes, notably V8 (used by Chrome and Node.js) support a nonstandard (as of ECMAScript 5) property called __proto__
. Assigning to the __proto__
property, even if done dynamically via obj[key] = {foo: 42}
changes the object's prototype, rather than merely giving it a new property named __proto__
. That also means if you've got an object with a plain __proto__
property (like when parsing JSON) and pass it to Oolong's assign
, it could inadvertently overwrite the target's prototype:
var O = require("oolong")
function Person(name) {
this.name = name
}
Person.prototype.greet = function() { return "Hi, " + this.name }
var john = new Person("John")
O.assign(john, JSON.parse("{\"__proto__\": {\"age\": 42}}"))
john.name // => "John"
john.age // => 42
john.greet // => undefined
In other situations, like when you're merging two objects recursively with merge
, this could cause the global prototype (Object.prototype
) to be modified.
As Oolong.js is written primarily for ECMAScript 5 compliant runtimes with no engine-specific workarounds, it doesn't have special handling for ignoring __proto__
. Unfortunately, even if it did, the presence of such special properties is far too likely to cause issues elsewhere to make a difference. It's quite common to assign dynamic values to object keys, e.g. when indexing an array (by creating an object with keys as values). Fortunately, you can and should disable __proto__
globally by overwriting it on the global Object.prototype
:
Object.defineProperty(Object.prototype, "__proto__", {
value: undefined, configurable: true, writable: true
})
After overwriting __proto__
on Object.prototype
, assigning, merging or cloning objects with __proto__
properties won't behave in any special manner. Assignments to __proto__
will become regular property assignments:
var john = new Person("John")
O.assign(john, JSON.parse("{\"__proto__\": {\"age\": 42}}"))
john.name // => "John"
john.age // => undefined
john.greet() // => "Hi, John"
john.__proto__ // => {age: 42}
Oolong.js is released under a Lesser GNU Affero General Public License, which in summary means:
- You can use this program for no cost.
- You can use this program for both personal and commercial reasons.
- You do not have to share your own program's code which uses this program.
- You have to share modifications (e.g. bug-fixes) you've made to this program.
For more convoluted language, see the LICENSE
file.
Andri Möll typed this and the code.
Monday Calendar supported the engineering work.
If you find Oolong.js needs improving, please don't hesitate to type to me now at [email protected] or create an issue online.