Oolong.js
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.
Installing
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
).
API
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)
Warning About __proto__
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}
License
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.
About
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 andri@dot.ee or create an issue online.