useless
Version:
Use Less. Do More. JavaScript on steroids.
93 lines (69 loc) • 4.28 kB
JavaScript
;
const _ = require ('underscore')
/* Properties
======================================================================== */
_.withTest ('properties', function () { var obj = {}
_.defineProperty (obj, 'fourtyTwo', 42)
_.defineProperty (obj, 'fourtyTwo_too', function () { return 42 })
_.defineProperty (obj, 'fourtyTwo_orDie', function (x) { $assert (x == 42); return 42 })
_.defineProperty (obj, 'fourtyTwo_eitherWay', {
configurable: true,
get: function () { return 42 },
set: function (x) { $assert (x == 42) } })
$assert (42,
obj.fourtyTwo,
obj.fourtyTwo_too,
obj.fourtyTwo_orDie (42),
obj.fourtyTwo_eitherWay = 42)
delete obj.fourtyTwo_eitherWay // can be deleted if configurable:true
$assert (obj.fourtyTwo_eitherWay === undefined)
$assertThrows (() => delete obj.fourtyTwo) // cannot be deleted (as default behavior)
_.defineHiddenProperty (obj, 'hiddenAndDangerous', 42) // shortut for enumerable:false
$assert (_.keys (obj).indexOf ('hiddenAndDangerous') < 0)
$assertEveryCalledOnce (function (mkay) { // memoized property
_.defineMemoizedProperty (obj, '_42', function () {
mkay (); return 42 })
$assert (
obj._42,
obj._42,
obj._42, 42) }) }, function () { _.extend (_, {
defineProperty: function (targetObject, name, def, defaultCfg) {
if (_.isObject (targetObject) && targetObject.hasOwnProperty (name)) {
throw new Error ('_.defineProperty: targetObject already has property ' + name) }
else {
Object.defineProperty (targetObject, name,
_.extend ({ enumerable: true }, defaultCfg, _.coerceToPropertyDefinition (def, name))) } },
defineHiddenProperty: function (targetObject, name, def, defaultCfg) {
return _.defineProperty (targetObject, name, def, _.extend ({ enumerable: false }, defaultCfg)) },
defineMemoizedProperty: function (targetObject, name, def_, defaultCfg) {
var def = _.coerceToPropertyDefinition (def_, name)
return _.defineProperty (targetObject, name,
_.extend ({}, def, {
get: _.memoizeToThis ('_' + name, def.get) }), defaultCfg) },
defineProperties: function (targetObject, properties) {
_.each (properties, _.defineProperty.partial (targetObject).flip2) },
memoizedState: function (obj) {
return _.filter2 (obj, function (v, k) { return (k[0] === '_') && !_.isFunction (v) }) },
memoizeToThis: function (name, fn) {
return function () {
var memo = this[name]
return (memo !== undefined) ? memo : (this[name] = fn.call (this)) } },
coerceToPropertyDefinition: function (value_, /* optional */ name) {
var value = value_ || {}
var actualValue = Meta.unwrap (value_)
var tags = Meta.tags (value_)
// property definition case (short circuit then)
return (!tags.constant && !tags.get && _.isPropertyDefinition (actualValue) && actualValue) ||
// get-accessor-alone case
((tags.get || (!tags.constant && _.isFunction (actualValue) && _.noArgs (actualValue))) &&
{ get: actualValue,
set: _.throwsError ('cannot change ' + (name || 'property') + ' (as it\'s an accessor function)') }) ||
// constant value case
(!tags.get && { get: _.constant (actualValue),
set: _.throwsError ('cannot change ' + (name || 'property') + ' (as it\'s sealed to ' + actualValue + ')') }) ||
// any other case (erroneous)
_.throwsError ('coerceToPropertyDefinition: crazy input, unable to match') () },
isPropertyDefinition: function (obj) {
return _.isObject (obj) && (_.isFunction (obj.get) || _.isFunction (obj.set)) },
ownProperties: function (obj) {
return (obj && _.pickKeys (obj, obj.hasOwnProperty.bind (obj))) || {} } }) })