Created
April 15, 2010 13:29
-
-
Save DmitrySoshnikov/367080 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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