Javascript: Using accessor properties in object closures - javascript

I recently learned about composing objects together using functions from reading this article. Following along, I end up with this code:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
Everything here works. However, I would like to be able to call isFlying as a property instead of a function:
// current (working)
bird.isFlying() // return value of `_isFlying`
// desired
bird.isFlying // return value of `_isFlying`
I know that get and set are keywords that can be used in object literals, and so I tried this:
function withFlying(o) {
let _isFlying = false
return {
...
get isFlying () {
return _isFlying
}
}
}
But it doesn't show the correct value after updating using the other functions. I figured that with the get property being a function, closures would apply similar to the other functions. Am I wrong in this assumption? Is there underlying behavior with get that I'm not understanding, and is what I'm trying to achieve possible the way I'm doing it now?
Here's a snippet with the code I tried to use:
function withFlying(o) {
let _isFlying = false;
return {
...o,
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
valueOf_isFlying() {
return _isFlying;
},
get isFlying () {
return _isFlying
}
}
};
function withWalking(o) {
let isWalking = false;
return {
...o,
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
}
}
const bird = withWalking(withFlying({}))
// desired
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // still returns false
console.log(bird.valueOf_isFlying()) // shows _isFlying is true

The problem is that when you create your new object, you're using spread notation to copy the properties from the original object:
return {
...o,
// ...
};
The problem with that is it copies the then-current value of accessor properties, not the definition of the accessor property. You can see that here:
const obj1 = {
get example() {
return 42;
}
};
console.log("Notice that the property descriptor is for an accessor property:");
console.log(Object.getOwnPropertyDescriptor(obj1, "example"));
const obj2 = {...obj1};
console.log("Notice that the property descriptor is for a simple data property:");
console.log(Object.getOwnPropertyDescriptor(obj2, "example"));
.as-console-wrapper {
max-height: 100% !important;
}
It's very much as though you did:
for (const key of Object.keys(o) {
newObject[key] = e[key];
}
e[key] gets the then-current value of the property, not the definition of the property.
To fix it, use Object.getOwnPropertyDesciptors to get the descriptors of the properties, and use Object.defineProperties to define those same properties on the new object. Since you're doing that (and adding more properties) in at least two places, you probably want a utility function:
function assignPropertyDescriptors(target, obj, updates) {
// B
return Object.defineProperties(
// A
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(obj)
),
Object.getOwnPropertyDescriptors(updates)
);
}
The "A" Object.defineProperties call copies the original object's property descriptors and applies them to the new object. The "B" Object.defineProperties call applies the ones you're adding to that new object as well.
But let's generalize that into a loop, similar to Object.assign (hence the name assignPropertyDescriptors):
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
withFlying and withWalking would then use that worker function, for instance:
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
Here's a complete example:
function assignPropertyDescriptors(target, ...updates) {
for (const update of updates) {
Object.defineProperties(
target,
Object.getOwnPropertyDescriptors(update)
);
}
return target;
}
function withFlying(o) {
let _isFlying = false;
return assignPropertyDescriptors({}, o, {
fly () {
_isFlying = true;
},
land () {
_isFlying = false;
},
get isFlying () {
return _isFlying
}
});
};
function withWalking(o) {
let isWalking = false;
return assignPropertyDescriptors({}, o, {
startWalking() {
isWalking = true;
return this
},
stopWalking() {
isWalking = false;
return this
},
isWalking: () => isWalking
});
}
const bird = withWalking(withFlying({}))
console.log(bird.isFlying) // _isFlying starts false
bird.fly() // should set _isFlying to true
console.log(bird.isFlying) // _isFlying is true

Related

Create Proxy for accessing json object values using .value

Example
Json
let Settings = {
"Policy": {
"allowP": true,
"allowC": false,
}
}
How can i access get value using following syntax
console.log( Policy.allowP ); // should return true
console.log( Policy.value.allowP ); // should also return true
i tried following using javascript proxy
let createProxy = (settings : any) => {
const p = new Proxy(settings, {
get(target, p, _r) {
debugger;
if (p === "value") {
return settings;
}
return (target as any)[p];
},
});
return p;
}
let getSettings = () => {
var settings = Settings;
const p = new Proxy(settings, {
get(target, p, _r) {
debugger;
if (p === "value") {
return createProxy(settings);
}
return createProxy((target as any)[p]);
},
});
return p;
};
Tried to create Nested proxy so that i can access values using key.value syntax.
Is this correct way to implement it ? or is there any better way ?

How to make it so when an object is called without calling any of its properties, it returns a default value

So I have an object called settings with a property called hex, which has its own properties:
var settings = {
hex: {
hex: "4fdaef",
validate: function(){
if(this.hex.length == 6){
return true
}
}
}
}
So currently to get the value of hex I would have to call settings.hex.hex, however ideally I would prefer to be able to call just settings.hex to get the value of the hex. How would I achieve this?
You'll have to rename hex to _hex, but this will work:
var settings = {
get hex() {
return this._hex.hex;
},
_hex: {
hex: "4fdaef",
validate: function () {
if (this.hex.length == 6) {
return true
}
}
}
}
console.log(settings.hex); // 4fdaef
With a Proxy you can allow for settings.hex.validate() to call _settings._hex._hex.validate(), but it's getting real ugly real quick, and we haven't even yet implemented the setter necessary for expected behavior of settings.hex = 'some other color'.
var _settings = {
_hex: {
_hex: new String('4fdaef'),
validate: function () {
if (this.length == 6) {
return true;
}
},
}
}
_settings._hex.hex = new Proxy(_settings._hex._hex, {
get(target, property) {
return property == 'validate' ? _settings._hex.validate : target[property];
}
});
const settings = new Proxy(_settings, {
get(target, property) {
return property == 'hex' ? target._hex.hex : target[property];
}
});
console.log(settings.hex); // [String: '4fdaef']
console.log(settings.hex.validate()); // true

Recursion mess while checking object properties

I have an object which could be nested as deep as possible. I'm trying to determine if object's property ready has at least one false value. If so the checkForFalse function should return false. I got confused while using recursion to solve this problem. What recursion call should return to make this code work? Or I'm completely wrong and missing something?
var obj = {
"currentServiceContractId": {
"ready": true,
"customerPersonId": {
"ready": false
}
},
"siteId": {
"ready": true
},
"districtId": {},
"localityId": {
"ready": true
},
"streetId": {
"ready": true
}
};
function checkForFalse(mainObj) {
let ans = _.find(mainObj || obj, (val) => {
if (_.keys(val).length > 1) {
let readyObj = _.pick(val, 'ready');
return checkForFalse(readyObj);
} else {
return _.get(val, 'ready') === false;
}
});
return _.isEmpty(ans);
}
console.log(checkForFalse(obj));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
This solution uses _.every() recursively to search for ready: false. The _.every() method will return immediately when the callback returns false:
function checkForAllReady(mainObj) {
return _.every(mainObj, (value, key) => {
if(key === 'ready' && value === false) {
return false;
}
if(_.isObject(value)) {
return checkForAllReady(value);
}
return true;
});
}
const obj = {"currentServiceContractId":{"ready":true,"customerPersonId":{"ready":true}},"siteId":{"ready":true},"districtId":{},"localityId":{"ready":true},"streetId":{"ready":true}};
console.log(checkForAllReady(obj));
const objWithFalse = _.merge({}, obj, { "streetId":{"ready":false} })
console.log(checkForAllReady(objWithFalse));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Encapsulate an object to when a `set`-like method is called, it's executed and the object itself is returned (pretty like JQuery does)

I'm trying to abstract a JQuery's common behavior that allows me to compose code easier: encapsulate an object to when a set-like method is called, it's executed and the object itself is returned (pretty like JQuery does).
Look:
let encapsulate = function (guy) {
return function () {
guy.call(this, [].slice.apply(arguments))
Object.setPrototypeOf(this, guy.prototype)
Object.getOwnPropertyNames(guy.prototype)
.filter(propName => propName.match(/set/g))
.forEach(propName => {
this[propName] = () => {
guy.prototype[propName].apply(this, [].slice.apply(arguments))
return this
}
})
}
}
A test case is:
// works
let date = new Date();
console.log(date.setDate(10)); // logs 1494447383428
console.log(date.getDate()); // logs 10
console.log(Date.prototype.setDate.apply(date, [20])); // logs 1494447383428
console.log(date.getDate()); // logs 20
// does not work
let ED = encapsulate(Date);
let date1 = new ED();
console.log(date1.setDate(10)); // should log date1 object but throws an error
Which throws an error Method Date.prototype.setDate called on incompatible receiver [object Object].
Could you help me? D:
(Updating) New advices from Bergi:
let encapsulate = function (guy) {
return function () {
this.inner = new (Function.prototype.bind.apply(guy, arguments))
let prototype =
Object.getOwnPropertyNames(guy.prototype)
.reduce((proto, propName) => {
let method = propName.match(/^set/g) ?
(function () {
guy.prototype[propName].apply(this.inner, [].slice.apply(arguments))
return this
}).bind(this) :
(function () {
return guy.prototype[propName].apply(this.inner, [].slice.apply(arguments))
}).bind(this)
return Object.defineProperty(proto, propName, {
value: method,
enumerable: false,
writable: false,
configurable: false
})
}, {})
Object.defineProperty(prototype, 'applyFunction', {
value: fn => { fn(this); return this },
enumerable: false,
writable: false,
configurable: false
})
Object.setPrototypeOf(this, prototype)
}
}
(Deprecated) Following what Bergi has said, I've done this, using composition:
let encapsulate = function (guy) {
return function () {
let argumentsWrapper =
[].slice.apply(arguments)
.reduce((aw, argument, idx) => {
aw['a' + idx] = argument;
return aw;
}, {})
this.inner = eval('new guy(' +
Object.keys(argumentsWrapper)
.reduce((string, argumentName, idx, arr) =>
string + 'argumentsWrapper.'
+ argumentName
+ (idx != arr.length - 1 ? ',' : ''),
'')
+ ')')
let setProperties = Object.getOwnPropertyNames(guy.prototype)
.filter(propName => propName.match(/^set/g))
setProperties.forEach(function (propName) {
this[propName] = (function () {
guy.prototype[propName].apply(this.inner, [].slice.apply(arguments))
return this
}).bind(this)
}, this)
Object.getOwnPropertyNames(guy.prototype)
.filter(propName => !propName.match(/^set/g))
.forEach(function (propName) {
this[propName] = (function () {
return guy.prototype[propName].apply(this.inner, [].slice.apply(arguments))
}).bind(this)
}, this)
this.applyFunction = fn => {
fn(this)
return this
}
}
}
It's not beautiful :/

Call functions from function inside an object in a javascript loop

I have a javascript object with some functions inside, I wish I could call them in a loop, something like this:
funcs: {
func1: function() {
return true;
},
func2: function() {
return false;
}
}
for(func in funcs) {
console.log(funcs[func]());
console.log(funcs[func].call());
}
Both work. But the declaration of your object is not correct. It is var object = { /*something*/};
var funcs = {
func1: function() {
return true;
},
func2: function() {
return false;
}
};
for(func in funcs) {
console.log(funcs[func]());
console.log(funcs[func].call());
}
Output
true
true
false
false

Categories

Resources