Object.defineProperty get set return wrong value - javascript

Say I have an object instance like this :
var objectA = {"a": 1, "b": 2, "c" : 3};
and in my code I access the property like this:
cc.log(objectA.a); // output 1
now I want to add a get/set for this object to provide some simple encrypt/decrypt feature:
hookSetGet: function (someObject) {
for (var key in someObject) {
cc.log("key: " + key);
// store the origin value before Object.defineProperty
var pureValue = someObject[key];
// add a property to store the encrypted value
var hiddenValueKey = "__" + key;
someObject[hiddenValueKey] = undefined;
Object.defineProperty (
someObject,
key,
{
set: function (val) {
// simulate encrypt
this.hiddenValueKey = val + 1;
cc.log("hooked set: " + val + " - " + this.hiddenValueKey);
},
get: function () {
cc.log("hooked get: " + this.hiddenValueKey + " - " + (this.hiddenValueKey - 1));
// simulate decrypt
return this.hiddenValueKey - 1;
}
}
);
// trigger set to encrypt
someObject[key] = pureValue;
}
}
but when I test the function like this:
var objectA = {"a": 1, "b": 2, "c" : 3};
this.hookSetGet(objectA);
cc.log(objectA.a);
cc.log(objectA.b);
cc.log(objectA.c);
I do not get the result I want :
key: a
hooked set: 1 - 2
key: b
hooked set: 2 - 3
key: c
hooked set: 3 - 4
hooked get: 4 - 3
3
hooked get: 4 - 3
3
hooked get: 4 - 3
3
It seems like even when I call
objectA.a
I will get the value of
objectA.c
The problem seems quite simple but I just can not figure out where is wrong.
Any suggestion will be appreciated, thanks :)
UPDATE:
I tried the following code without change the code of hookSetGet :
cc.log(objectA.__a);
cc.log(objectA.__b);
cc.log(objectA.__c);
and get:
undefined
undefined
undefined
Then I changed the hookSetGet function:
set: function (val) {
// simulate encrypt
someObject[hiddenValueKey] = val + 1;
cc.log("hooked set: " + val + " - " + someObject[hiddenValueKey]);
},
get: function () {
cc.log("hooked get: " + someObject[hiddenValueKey] + " - " + (someObject[hiddenValueKey] - 1));
// simulate decrypt
return someObject[hiddenValueKey] - 1;
}
I changed all the this.hiddenValueKey to someObject[hiddenValueKey].
and the output is :
cc.log(objectA.__a); // 2 good
cc.log(objectA.__b); // 3 good
cc.log(objectA.__c); // 4 good
cc.log(objectA.a); // hooked get: 4 - 3 still wrong
cc.log(objectA.b); // hooked get: 4 - 3 still wrong
cc.log(objectA.c); // hooked get: 4 - 3 still wrong

So, you wrote this:
Object.defineProperty (
someObject,
key,
{
set: function (val) {
// simulate encrypt
this.hiddenValueKey = val + 1;
cc.log("hooked set: " + val + " - " + this.hiddenValueKey);
},
get: function () {
cc.log("hooked get: " + this.hiddenValueKey + " - " + (this.hiddenValueKey - 1));
// simulate decrypt
return this.hiddenValueKey - 1;
}
}
);
In your getter and setter this from this.hiddenValueKey refers to your objectA Object in all cases, not to each property. So when you want to set a value for each property you're actually over-writing objectA.hiddenValueKey. This is why when you try to get back the values you only get the last value which was set.
Even though you set hiddenValueKey to be unique, in the getter and setter you acess the same property. This is because this.hiddenValueKey is the same as writing this['hiddenValueKey']. Did you mean to write this[hiddenValueKey] ? Even if you do it, you might have some scoping issues with the hiddenValueKey always having the latest key value after you exit the loop.
So, you can try this:
Object.defineProperty (
someObject,
key,
{
set: function (val) {
// simulate encrypt
this[hiddenValueKey] = val + 1;
cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
},
get: function () {
cc.log("hooked get: " + this[hiddenValueKey] + " - " + (this[hiddenValueKey] - 1));
// simulate decrypt
return this[hiddenValueKey] - 1;
}
}
);
But, as I said, you might have to create a closure for the hiddenValueKey variable so it will be unique for each property getter and setter.
You can create a closure like this:
(function(hiddenValueKey) {
Object.defineProperty (
someObject,
key,
{
set: function (val) {
// simulate encrypt
this[hiddenValueKey] = val + 1;
cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
},
get: function () {
cc.log("hooked get: " + this[hiddenValueKey] + " - " + (this[hiddenValueKey] - 1));
// simulate decrypt
return this[hiddenValueKey] - 1;
}
}
);
}(hiddenValueKey));

There are several issues with your code. One of them is that key and hiddenValueKey are set in the scope of the hookGetSet function. Therefore whenever you use them, you use the last value in the loop (3 and __c). You can fix this in two ways:
use let instead of var to define key and hiddenValueKey within the loop scope, but that only works in ES6
use a closure to scope the inside of the loop
The other problem is that inside the properties you use this.hiddenValueKey, which is the same as this['hiddenValueKey'], not this[hiddenValueKey] as I assume you intended.
Here is code that works (EcmaScript6):
hookSetGet : function (someObject) {
for (let key in someObject) {
cc.log("key: " + key);
// store the origin value before Object.defineProperty
var pureValue = someObject[key];
// add a property to store the encrypted value
let hiddenValueKey = "__" + key;
someObject[hiddenValueKey] = undefined;
Object.defineProperty(
someObject,
key, {
set : function (val) {
// simulate encrypt
this[hiddenValueKey] = val + 1000;
cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
},
get : function () {
// simulate decrypt
var result = this[hiddenValueKey] - 1000;
cc.log("hooked get: " + this[hiddenValueKey] + " - " + result);
return result;
}
});
// trigger set to encrypt
someObject[key] = pureValue;
}
}
and here is same code for classic ES5 Javascript:
hookSetGet : function (someObject) {
for (var k in someObject) {
(function () {
var key = k;
cc.log("key: " + key);
// store the origin value before Object.defineProperty
var pureValue = someObject[key];
// add a property to store the encrypted value
var hiddenValueKey = "__" + key;
someObject[hiddenValueKey] = undefined;
Object.defineProperty(
someObject,
key, {
set : function (val) {
// simulate encrypt
this[hiddenValueKey] = val + 1000;
cc.log("hooked set: " + val + " - " + this[hiddenValueKey]);
},
get : function () {
// simulate decrypt
var result = this[hiddenValueKey] - 1000;
cc.log("hooked get: " + this[hiddenValueKey] + " - " + result);
return result;
}
});
// trigger set to encrypt
someObject[key] = pureValue;
})();
}
}

Related

Subtract method in object if greater than zero

I'm using a game object that has different getters and methods within it. One of the parameters of my assignment is that the "playerDies" method has to subtract 1 from the lives if it is greater than zero and it cannot go below 0 (so it can't be -1 which is what is happening now). This topic is fairly new to me so I'm not sure what I can do to execute the subtraction only when there is a specific value. I've tried using an if-else statement but that resulted in the code not working.
My code:
var game = {
lives: 3,
coins: 0,
get points() {
return this.coins * 10;
},
playerDies: function() {
return this.lives -= 1;
},
newGame: function() {
this.lives = 3;
this.coins = 0;
},
};
console.log("lives = " + game.lives); // should be 3
console.log("coins = " + game.coins); // should be 0
console.log("points = " + game.points); // should be 0
game.coins = 0;
console.log("points = " + game.points); // should be 20
game.playerDies();
console.log("lives = " + game.lives); // should be 2
game.playerDies();
game.playerDies();
game.playerDies();
console.log("lives = " + game.lives); // should be 0
game.newGame();
console.log("lives = " + game.lives); // should be 3
console.log("coins = " + game.coins); // should be 0
You can put if statement in your playerDies method
playerDies: function() {
if (this.lives > 0) {
return this.lives -= 1
}
return 0
}

Create function to iterate through object key

I am playing around with binance API, im very new to javascript, the first section in my code
binance.prices((error, ticker) => {
console.log("prices()", ticker);
console.log("Price of BTC: ", ticker.BTCUSDT);
});
above code outputs:
ETHBTC: '0.07421500',
LTCBTC: '0.01994000',
BNBBTC: '0.00110540',
NEOBTC: '0.00853400',
QTUMETH: '0.02604400',
the code below runs a check on an selected key (GTOBTC), I cant seem to be able to create a loop which takes the name from the keys above.
binance.depth("GTOBTC", (error, depth, symbol) => {
a = 0;
b = 0;
for (value in depth.bids){
a += Number(value);
};
for (value in depth.asks){
b += Number(value);
};
var c = a - b;
var d = (c / a) * 100;
if (d >= 2.0){
console.log(symbol + " Percent ok");
console.log(d);
} else {
console.log(symbol + " percentage not sufficient");
}
})
output for code above:
GTOBTC percentage not sufficient
Any help would be great thanks.
You can use Object.keys as below:
Object.keys(object).map((key) => {
console.log(object[key])
});
or when you have jquery in web, u can use this:
$.each(object, function(key, value) {
console.log(key + ' ' + value);
});

Declaring variable within functions

Ok, so I I'm having this strange behaviour that I cannot explain. Look at the following:
$("#completeData").on("click", function() {
var toUpdate = {};
var toUpdateCount = 0;
var ratios = {};
This.calculateGradePerSize();
//1) Select all sizes that are equal to NA or are Equal to 0 (means its a new one)
$.each(This.logements, function(key, l) {
if (l.sizeMyId === "NA" || l.sizeMyId === 0) {
toUpdate[l.rueNum] = l;
toUpdateCount++;
} else { //else init the ratios because it means they are actually present
/**
//My problem is this variable,
I want it to be equal to an empty object
But for reasons I cannot seem to understand,
it takes in account the latter modification in the code
that happens to this variables
*/
ratios[l.sizeMyId] = {};
}
});
console.log(toUpdate);
console.log(ratios);
console.log(This.sizeRatio);
//2) Calculate Ratios and build the ratios function of the toUpdate
$.each(This.sizeRatio, function(sizeMyId, count) {
if (sizeMyId !== "NA" && sizeMyId != 0) {
console.log("COUNT SIZE: " + count + " COUNT LOGEMENT: " + This.countLogement + " toUpdateCount: " + toUpdateCount + " SizeMyId: " + sizeMyId);
console.log("Calculation: " + count / This.countLogement * toUpdateCount);
ratios[sizeMyId].count = Math.ceil(count / This.countLogement * toUpdateCount);
console.log("Calculation WITH CEIL: " + Math.ceil(count / This.countLogement * toUpdateCount));
ratios[sizeMyId].grade = This.sizeGrade[sizeMyId];
ratios[sizeMyId].sizeMyId = sizeMyId;
}
});
console.log(ratios);
});
As explained in the multiline comment, my problem is the ratio variable. I tried declaring the variable without var prefix, so that JS doesn't know its existence but still, I want it to be empty object. In fact, the problem has stronger roots than simply that, I cannot update it. Each change I make to the ratios var are not registered, but I wanna start with the beginning how can I make sure that this variable is empty at the beginning of the function.
I don't know if this question is really worth. Thinking about deleting it. My bug was that the count variable in the each function as well as the ratio definition were the same hence not registering.
As for the variable not being an empty one at function start. It simply how the JS engine works. If there is something not working, more likely than not, there is something wrong in your code.
$.each(This.sizeRatio, function (sizeMyId, count) {
if (sizeMyId !== "NA" && sizeMyId != 0) {
console.log("COUNT SIZE: " + count + " COUNT LOGEMENT: " + This.countLogement + " toUpdateCount: " + toUpdateCount + " SizeMyId: " + sizeMyId);
console.log("Calculation: " + count / This.countLogement * toUpdateCount);
//HERE ratios[sizeMyId].count IS THE SAME than the anonymous function.
ratios[sizeMyId].count = Math.ceil(count / This.countLogement * toUpdateCount);
console.log("Calculation WITH CEIL: " + Math.ceil(count / This.countLogement * toUpdateCount));
ratios[sizeMyId].grade = This.sizeGrade[sizeMyId];
ratios[sizeMyId].sizeMyId = sizeMyId;
}
});

Saving data using promises

On Parse.com I have the following function, and my question follows:
function myFunction(array, value) {
var logMessage;
logMessage = "array: " + array.length.toString();
console.log(logMessage);
if (!array.length) return;
if (!value) value = Math.min(10, array.length);
if (array[array.length - 1].get("advertisePointNumber") >= value) return;
var classPromise;
array[array.length - 1].set("advertisePointNumber", value);
logMessage = "(BIS)array: " + array.length.toString();
console.log(logMessage);
classPromise = (array[array.length - 1].save(null, {}).then(function (object) {
logMessage = "HERE I AM!!!";
console.log(logMessage);
if (array.length == 1) return;
array.splice(array.length - 1, 1);
return myFunction(array, value);
}, function (error) {
// saving the object failed.
console.log("error:" + error);
}));
logMessage = "(TER)array: " + array.length.toString();
console.log(logMessage);
return Parse.Promise.when(classPromise);
}
The question is what am I missing? I never see the message "HERE I AM!!!" (and no error either) in the logs and as a consequence the recursive call that I wish is not working.
I have successfully used similar code in the past, when reading data. Here I am saving data. I must be doing something the wrong way. What is it?
Update to the question:
Calling this function with the following line of code:
myFunction(myArray, 0);
I get the log below:
I2015-06-22T07:05:34.160Z]myArray: 2 // Number of elements in the initial Array.
I2015-06-22T07:05:34.161Z]array: 2
I2015-06-22T07:05:34.162Z](BIS)array: 2
I2015-06-22T07:05:34.163Z](TER)array: 2
I would expect to see :
I2015-06-22T07:0.....]array: 1
after the above but I do not see anything.
Instead of going for recursive, you can try this:
classPromise = array.map(function(obj){ return obj.save();});
in es6, same thing can be:
classPromise = array.map(obj => obj.save());
Edit
You can reduce the whole function to:
function myFunction(array, value) {
if ( !array || !array.length) return;
console.log("array: " + array.length);
if (!value) value = Math.min(10, array.length);
var pSave=[], i = array.length, v2=(value)? value : Math.min(10, i);
while(i>0 && array[i-1].get("advertisePointNumber") >= value){
array[i - 1].set("advertisePointNumber", value);
pSave.push(array[i - 1].save());
console.log("(BIS)array: " + i);
i--;
v2=(value)? value : Math.min(10, i);
}
return Parse.Promise.when(pSave);
}
if you wanted it to be saved sequentially,
...
var pSave = Parse.Promise.as(1),
...
pSave.then(function(){
return array[i-1].save();
});
...
return pSave;
}
You don't need to pass null as the 1st parameter
You don't need to use both save with options (2nd parameter), and save with promises chain (.then)
So, just remove both the first and the second parameter of save function as follows
array[array.length - 1].save().then(...
Updated Answer
You should use multiple save calls in promises chain instead of recursive one like the below
function myFunction(array, value) {
var logMessage;
logMessage = "array: " + array.length.toString();
console.log(logMessage);
if (!array.length) return;
if (!value) value = Math.min(10, array.length);
var savePromises = Parse.Promise.as();
array.map(function(element, index) {
// Calculate value based on your needs
if(element.get("advertisePointNumber") < value) {
element.set("advertisePointNumber", value);
}
savePromises = savePromises.then(function() {
return element.save();
});
});
return savePromises.then(function (object) {
logMessage = "HERE I AM!!!";
console.log(logMessage);
}, function (error) {
// saving the object failed.
console.log("error:" + error);
});
}
OR if you have the option to make cloud code to save them in one request, you should do, to reduce network requests

Access arguments of a function passed as argument

I'm trying to flip the parameters of a function passed to another function:
function dash(a,b) {
return a + " - " + b;
}
function flipArgs(fn) {
return fn;
}
flipArgs(dash)(1,2);
Currently it returns "1 - 2", I need to return "2 - 1". How can I access to the arguments passed to my "flipArgs" function?
You can do this :
function flipArgs(fn) {
return function(){
return fn(arguments[1], arguments[0]);
}
}
Of course, depending on your needs, you could use a larger swap, or test arguments.length.
#dystroy answer is great, but there is a more generic way, take a look:
function dash(a,b,c) {
return a + " - " + b + " - " + c;
}
function flipArgs(fn) {
return function(){
return fn.apply(this, Array.prototype.reverse.call(arguments));
}
}
var r = flipArgs(dash)(1,2,3);
console.log(r); // 3 - 2 - 1
JSFiddle: http://jsfiddle.net/u3f4t919/1/

Categories

Resources