Skip to content

Instantly share code, notes, and snippets.

@draeton
Created August 16, 2011 04:14
Show Gist options
  • Save draeton/1148428 to your computer and use it in GitHub Desktop.
Save draeton/1148428 to your computer and use it in GitHub Desktop.
extend objects or functions from right to left with properties from other objects
(function () {
var toString = Object.prototype.toString,
slice = Array.prototype.slice,
/**
* determines type of passed argument
*/
toType = function (thing) {
return toString.call(thing).match(/\s(\w+)/)[1].toLowerCase();
},
/**
* toType curry
*/
is = function (type) {
return function (thing) {
return toType(thing) === type;
};
},
/**
* is this an instance of the Object prototype or its descendants
*/
isObject = function (thing) {
return thing instanceof Object;
},
/**
* toType shortcuts
*/
isPlainObject = is('object'),
isFunction = is('function'),
isArray = is('array'),
isNumber = is('number'),
isBoolean = is('boolean');
/**
* extend objects or functions from right to left with properties from other
* objects, and return the first extended argument; used for applying options
* to defaults, or extending classes or constructors
* @return {Object|Function}
*/
Object.extend = function () {
var args = slice.call(arguments), // convert arguments object to Array
ret = null,
o = null,
cur = null,
i = null,
j = null,
deep = false,
stack = [];
// if the first arg is boolean, it determines if we are deep copying objects
// if it is boolean, remove it from args for further processing of args
if (isBoolean(args[0])) {
deep = args.shift();
}
// push the remaining arguments onto the stack
stack.push(args);
// the return value object will be the first remaining argument
ret = args[0];
// only allow processing of objects, and the remaining
// args length must be greater than 1 to have something to copy
if (isObject(ret) && args.length > 1) {
// get the next item from stack and set as args
// args is an array of objects
while (args = stack.shift()) {
// o is the item we are copying all values into
o = args.shift();
// get the next item from args and set as cur
// cur is an object
while (cur = args.shift()) {
// check for objects only
if (!(isObject(cur))) {
throw new TypeError("Argument must be an object");
} else {
// loop through properties i
// if cur has own property i, proceed
for (i in cur) {
if (cur.hasOwnProperty(i)) {
// if this is a deep copy, and o[i] & cur[i] are objects
// add o[i] and cur[i] to the stack to override
// properties instead of replacing the whole object
if (deep && isObject(o[i]) && isObject(cur[i])) {
stack.push([o[i], cur[i]]);
} else {
// if cur[i] is an object, clone and replace o[i]
// else, set o[i] to cur[i]
if (isObject(cur[i])) {
if (!isObject(o[i])) {
o[i] = {};
}
stack.push([o[i], cur[i]]);
} else {
o[i] = cur[i];
}
}
}
}
}
}
}
}
return ret;
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment