Skip to content

Instantly share code, notes, and snippets.

@DmitrySoshnikov
Created April 15, 2010 13:29
Show Gist options
  • Save DmitrySoshnikov/367080 to your computer and use it in GitHub Desktop.
Save DmitrySoshnikov/367080 to your computer and use it in GitHub Desktop.
/**
* Aliases and additional "sugar"
* 2010-04-15
* @author Dmitry A. Soshnikov
*/
(function () {
var
$break = {},
hasOwn = Object.prototype.hasOwnProperty;
// provide aliases for methods from ES5
// put them into Object.ptototype with [[Enumerable]] false
["defineProperty", "defineProperties", "keys", "getOwnPropertyNames",
"getOwnPropertyDescriptor", "getPrototypeOf", "seal", "freeze",
"preventExtensions", "isFrozen", "isSealed", "isExtensible"]
.forEach(function (methodName) {
var newMethodName = (
methodName == "getPrototypeOf"
? "getPrototype"
: methodName
);
Object.defineProperty(Object.prototype, newMethodName, {
// use function (as in spec), but for some properties -
// getter is better, e.g. o.keys - for what to make it as a method?
value: function () {
return Object[methodName].apply(
Object,
[this].concat(Array.slice.call(arguments))
);
}
});
});
// additional sugar
Object.defineProperties(Object.prototype, {
/**
* extend
* @param {Object} mixin
*/
extend: {
value: function extend(mixin) {
mixin.getOwnPropertyNames().forEach(function (name) {
if (hasOwn.call(this, name))
return;
this.defineProperty(
name,
mixin.getOwnPropertyDescriptor(name)
);
}, this);
return this;
}
},
/**
* getDescriptorOf
* returns extended descriptor:
* nativeDescrptor + {own: {Boolean}, owner: {Object}}
*/
getDescriptorOf: {
value: function getDescriptorOf(propertyName) {
var extDesc;
if (this.hasOwnProperty(propertyName)) {
extDesc = this.getOwnPropertyDescriptor(propertyName);
extDesc.owner = this;
extDesc.own = true
} else {
// find first inherited with this name
extDesc = this.find(function findFn(propObj) {
return propObj.name == propertyName;
});
delete extDesc.name;
extDesc.own = false;
}
return extDesc;
}
},
/**
* forAll
* generic method to iterate object;
* iterates over all properties of an object
* including those with [[Enumerable]] false,
* analysing prototype chain as well
* @param {Function} iteration/conditional funArg
* @param {Object} searchInfo {all: false}
*/
forAll: {
value: function forAll(fn) {
var
self = this,
object = this;
do {
object.getOwnPropertyNames().forEach(function (name) {
var propertyObject = {
name: name,
value: object[name],
owner: object
}
// extend propertyObject with native descriptor
.extend(object.getOwnPropertyDescriptor(name));
if (!fn.call(self, propertyObject))
return false;
});
} while (object = object.getPrototype());
return true;
}
},
find: {
value: function find(fn, all, collectIterResults) {
var
result,
iterResult,
results = all ? [] : null;
try {
this.forAll(function (propObj) {
if (iterResult = fn.call(this, propObj)) {
result = collectIterResults ? iterResult : propObj;
if (!all)
throw $break;
results.push(result);
}
});
} catch (e if e == $break) {
return result;
}
return results;
}
},
/**
* findAll
* generic function
* @param {Function} fn
* @param {Boolean} all
*/
findAll: {
value: function find(fn, collectIterResults) {
return this.find(fn, true, collectIterResults);
}
},
/**
* getEntities {values or properties}
* @param {Variant} obj {Object} or {Function}
* @param {String} entityName
*/
getEntities: {
value: function getEntities(entityName, obj) {
!entityName && (entityName = "name");
!obj && (obj = {});
if (typeof obj == "boolean")
obj = {own: !obj};
if (!hasOwn.call(obj, "own"))
obj.own = true;
// need more cheks, but for now -- just an example
if (!Array.isArray(obj.enumerable) && obj.enumerable != "both")
obj.enumerable = true;
return this.findAll(function (propObj) {
var bRes = true; // boolean result
// only own
if (obj.own)
bRes = bRes && (propObj.owner == this);
if (typeof obj.enumerable == "boolean")
bRes = bRes && (propObj.enumerable == obj.enumerable);
if (bRes) {
return propObj[entityName];
}
return false;
}, true);
}
},
/**
* see getEntities
*/
getPropertyNames: {
value: function getPropertyNames(obj) {
return this.getEntities("name", obj);
}
},
/**
* see getEntities
*/
getValues: {
value: function getValues(obj) {
return this.getEntities("value", obj);
}
},
/**
* inspect
* @param {Object} inspectInfo {deep: {Boolean}, descriptors: {Boolean}}
*/
inspect: function inspect(inspectInfo) {
//
}
});
})();
var o = {x: 10, y: 20};
o.defineProperty("z", {value: 100});
print(o.getPropertyNames()); // x,y, but not z
print(o.z); // 100
print(o.getPropertyNames({
enumerable: [true, false]
})); // x,y,z
print(o.getValues()); // 10, 20, but not 30
// the same
print(o.getPropertyNames({
enumerable: "both"
})); // x,y,z
print(o.getValues({
enumerable: [true, false]
})); // 10, 20, 30
// all, including inherited
print(o.getPropertyNames(true));
// all, including inherited and with [[Enumerable]] = false
print(o.getPropertyNames({
own: false,
enumerable: [true, false]
}));
// get only inherited properties
var inherited = o.findAll(function searchCond(propObject) {
return propObject.owner != this;
});
print(inherited.length);
print(inherited[0].enumerable, inherited[0].name, inherited[0].value, inherited[0].configurable);
var toStringDescriptor = o.getDescriptorOf("toString");
print(
toStringDescriptor.enumerable, // false
toStringDescriptor.writable, // true
toStringDescriptor.configurable, // true
toStringDescriptor.own, // false
toStringDescriptor.owner === o, // false
toStringDescriptor.owner === Object.prototype // true
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment