Was variable x ever true? - javascript

In Javascript, is there a good way to check if a variable was ever a true, (or any value), in the entire session? The best way I can think of right now is to perform a regular check like this, recording the truthiness in another variable:
if (variable){
variablewasevertrue = true;
}
Then when I want to know if the original variable was ever true, I check if the new variablewasevertrue is true or undefined. There's nothing more graceful like if (variable was ever true){ is there? That doesn't seem very Javascript-y.

No there is no if (variable was ever true) facility in the language. Variables store values, not history.
Intercepting values as they're assigned and checking is the only way to do it. If the variable is really a property (e.g. a global variable is a property of the global object) you can intercept changes easily using setters.
So to have a history keeping global variable you could do
var hasEverBeenTruthy = false;
(function () {
var value;
Object.defineProperty(window, "myGlobal", {
"get": function () { return value; },
"set": function (newval) {
if (newval) { hasEverBeenTruthy = true; }
value = newval;
}
});
})();
This will work on modern browsers, and there are __defineSetter__ variants on many older browsers.

Variables store value, not a history of a memory location. If you want to do something like this, I would suggest you use an Object of some sort:
var x = {
value: false,
history: [],
set: function(val){
history.push(this.value);
this.value = val;
},
wasEver: function(val){
return this.history.indexOf(val) >= 0;
}
};
Then you can use the object like so:
x.set(false);
x.value; //returns false
x.set(true);
x.value; //returns true
x.wasEver(true); // returns true
x.wasEver(false); //returns true
x.wasEver("hello"); //returns false
This gives each object it's own history (as in, it can check multiple values, not just one - as with the getter/setter stuff mentioned in other answers), and is guaranteed to work in any scope, as all functionality is contained within the defined object.

No, except that you could use a getter and setter like this, which delegates the setting of a variable so that you can check whether it is to set at one time:
var value,
wasevertrue = false;
window.__defineSetter__('test', function(v) {
value = v;
wasevertrue = wasevertrue || (v === true);
});
window.__defineGetter__('test', function() {
return value;
});
Now,
test = false; // wasevertrue === false
test = true; // wasevertrue === true
test = false; // wasevertrue === true
Better yet would be putting this in a closure because you can now just set value = true as a workaround to the setter.

no - there is no state tracking on variables. it is only whatever its current value is. beyond that its your own custom implementation using property-like methods for state tracking.

Have another variable called "wasevertrue = false." Anywhere you set "variable" immediately follow it with a check that sees if variable == true. If it is, set wasevertrue = true.

You can't use a single scalar variable to track history, but you could use an array. It's not ideal, but it's an alternative:
function setVar(arr, value) {
arr.unshift(value);
return arr;
}
function getVar(arr) {
return arr[0];
}
function checkHist(arr, histValue) {
var ii;
for (ii = 0; ii < arr.length; ii++) {
if (arr[ii] === histValue) {
return true;
}
}
return false;
}
var myVar = [];
myVar = setVar(myVar, true);
myVar = setVar(myVar, false);
alert(checkHist(myVar, true)); // alerts "true"

Related

Change a native function's body while keeping the same "identity"

I'm looking into a way to change a native JS function body, while making it not possible to see that it has been changed. Let's take an example with document.hasFocus():
document.hasFocus = ()=>true;
This method works well to spoof focus, but it can be easily detected that it was modified:
document.hasFocus.toString() // -> "()=>true"
Is there any way, in which I can modify such a function while making it impossible to see it has been tampered with?
You can overwrite toString method in Function prototype, and do something like that:
// https://stackoverflow.com/questions/1833588/javascript-clone-a-function
Function.prototype.clone = function() {
var that = this;
var temp = function temporary() {
return that.apply(this, arguments);
};
for (var key in this) {
if (this.hasOwnProperty(key)) {
temp[key] = this[key];
}
}
return temp;
};
Function.prototype.__oldToString = Function.prototype.toString.clone();
function __toStringHooked() {
if ((this.name == "")||(this.name == "hasFocus")) // on Firefox, hasFocus doesn't have any name
{
return eval+"" // this matches regexp
} else {
return this.__oldToString(); // we're returning default value
}
}
Function.prototype.toString = __toStringHooked
document.hasFocus = () => true
The code above is from Th3B0r3dD3v3l0p3r's GitHub repo, you can check it if you want: https://github.com/Th3B0r3dD3v3l0p3r/focus-spoofer/

Run Function on Variable Change

Let's assume I have a variable:
var x = 0;
Each time this variable gets modified I want to run a function:
function(){
console.log('x has been changed');
}
Would RxJs be appropiate for this task? If not, what other approach would work better?
You set value to property of an object, use set, get.
const x = {};
let value = 0;
function fn(oldValue, newValue) {
console.log(`${oldValue} has been changed to ${newValue}`);
}
Object.defineProperty(x, "prop", {
get() {
return value
},
set(val) {
fn(value, val);
value = val;
}
});
x.prop = 1;
x.prop = 10;
Douglas Tyler finished his answer before I had the chance to but yes, proxy is definitely something that you might use and here's an example :
const obj = {
_id: null,
set id(str) {
console.log('setting the value !');
this._id = str;
},
get id() {
return this._id;
}
}
I think a good bet would be to use Proxy, although this only works for objects, arrays and functions. Another option would be checking the value of on an interval and comparing it to the old value of x which you've stored in another variable, though this may not work for your purposes. I think your best option would be to always set x with a function that does whatever other functionality you want it to.
You can use interval. Although I use Angular to $watch, you can see its implementation. There is also object.watch functionality but last i checked was not working in other browsers. So below is code using intervals (not a fun of intervals though)
var x=0, xWatcher = 0;
setInterval(function() {
if ( x !== xWatcher ) {
xWatcher = x;
//Your code here
xChanged()
}
}, 50); // any delay you want
function xChanged(){
console.log('x has been changed');
}

AngularJS ignoring a key in an watched object or overriding the $watch listener

I'm deep watching a property that is bound to multiple controls:
$scope.$watch('config', function(){}, true);
the config itself contains various parameters:
scale
point
aggregates
current
I want to ignore changes to scale when it is changed by a specific control and a specific function.
Is there a way to ignore a specific property or override the watch is specific cases?
For now this is what i'm doing:
The dataChange now fires only on certain changes, in this case when other properties,
not zoom are changing.
In order to disable the dataChange for a specific zoom case i just assigned it to the rest of the cases.
I'm using Switch and not if/else just because it's more descriptive and easily extendable for more cases.
$scope.$watch('config', function(n,o,scope){
$scope.config = n;
if (n != o) {
switch(true){
case n.zoom != o.zoom:
break;
default:
$scope.dataChange($scope.dataTable);
};
}
}, true);
I don't like any of these answers. The first parameter of $watch is what to watch, which accepts the property name as a string, or a function to return the value. Simply use a function & return the value you want to watch. Here I use lodash JS library to $watch a new object that is based on the real object, but with the property stripped:
$scope.$watch(function() {
return _.omit($scope.config, 'scale');
}, function(oldVal, newVal) {
console.log(oldVal, newVal);
});
Without Lodash [blacklist properties]:
$scope.$watch(function() {
var myConfig = Angular.copy(config);
delete myConfig.scale;
return myConfig;
}, function(oldVal, newVal) {
console.log(oldVal, newVal);
});
Without Lodash [whitelist properties]:
$scope.$watch(function() {
return {
point: $scope.config.point,
aggregates: $scope.config.aggregates,
current: $scope.config.current
};
}, function(oldVal, newVal) {
console.log(oldVal, newVal);
});
In my opinion the other answers are not the "Angular way". This approach is not only more succinct than the other answers which are messy, but also avoids performing a redundant object comparison when the $watch fires. Keep in mind the other answers incur the cost of object comparison twice, once for the $watch itself in the Angular code, then you incur the cost of your "home made" object comparison in your callback function. My approach ensures the object comparison is only incurred once, in the Angular code, by stripping the unwanted property before feeding the object into the $watch for comparison.
Not as far as I know, but a simple check would do the trick:
$scope.$watch('config', function(newValue, oldValue){
if (newValue.scale == oldValue.scale) {
// ignore this
return;
}
// continue...
}, true);
Better solution can be that function;
$scope.equalsAdvanced=function (sourceObject, targetObject, ignoredProperties)
{
// direct compare if there is no ignored properties
if (!ignoredProperties || (angular.isArray(ignoredProperties) && ignoredProperties.length<=0)) {
return angular.equals(sourceObject, targetObject);
}
// take the original ignored property list to a new variable
var ignoredPropertyList=ignoredProperties;
// make it array if it is not
if (!angular.isArray(ignoredPropertyList)) {
var list = [];
list.push(ignoredPropertyList);
ignoredPropertyList = list;
}
// compare property list
for (propertyName in sourceObject) {
if (ignoredPropertyList.indexOf(propertyName) >= 0)
continue;
var sourceValue = sourceObject[propertyName];
var targeValue = targetObject[propertyName];
if (!angular.equals(sourceValue, targeValue))
return false;
}
return true;
};
You can check the example code on fiddle:
http://jsfiddle.net/tursoft/DpEwV/4/
This can be the better than previous;
CODE:
// service
myApp
.service("utils", function()
{
self=this;
// watchAdvanced =====================
self.$watchAdvanced = function ($scope, exp, ignoredProperties, callback)
{
$scope.$watch(exp, function (newValue, oldValue) {
if (self.equalsAdvanced(newValue, oldValue, ignoredProperties))
return;
callback(newValue, oldValue);
}, true);
}
// equalsAdvanced =====================
self.equalsAdvanced=function (sourceObject, targetObject, ignoredProperties)
{
// direct compare if there is no ignored properties
if (!ignoredProperties || (angular.isArray(ignoredProperties) && ignoredProperties.length<=0)) {
return angular.equals(sourceObject, targetObject);
}
// take the original ignored property list to a new variable
var ignoredPropertyList=ignoredProperties;
// make it array if it is not
if (!angular.isArray(ignoredPropertyList)) {
var list = [];
list.push(ignoredPropertyList);
ignoredPropertyList = list;
}
// compare property list
for (propertyName in sourceObject) {
if (ignoredPropertyList.indexOf(propertyName) >= 0)
continue;
var sourceValue = sourceObject[propertyName];
var targeValue = targetObject[propertyName];
if (!angular.equals(sourceValue, targeValue))
return false;
}
return true;
};
});
USAGE:
utils.$watchAdvanced($scope, "user", ["_State", "ID"], function(newValue, oldValue)
{
$scope.changeCount+=1;
$scope.logs.push($scope.changeCount + ": User object is changed!");
}, true);
Source Code on Fiddle:
http://jsfiddle.net/tursoft/5rLfr/2/

Value assignment and return variable in same line

I have following code
var s = storage('foo');
s.bar = 100;
storage('foo', s);
storage returns an object from some storage media. I then change the value of one property of the object and send it to storage again. My question is is there any way to make it a one liner? Something like
storage('foo', storage('foo').bar = 100);
But the above code only saves 100 instead of entire foo object.
Here is the storage function, it stores on localstorage but makes it object storage instead of string storage:
function storage(item, value) {
if(value!=null) {
var result = (storage()) ? storage() : {};
if(item) result[item] = value;
return localStorage.setItem('storage', JSON.stringify(result))
} else return (item) ? storage()[item] : JSON.parse(localStorage.getItem('storage'));
}
Edit:
So I ended up with following one line change in storage function. But this does not account for multidimensional usage. So I will welcome any suggestions.
function storage(item, value, property) {
...
if(item) (property) ? result[item][property] = value : result[item] = value;
...
}
The reasonably thing to do, if you cannot change the storage function, is to create another function:
function set_storage(obj, prop, val) {
var s = storage(obj);
obj[prop] = val;
storage(obj, s);
}
// usage
set_storage('foo', 'bar', 100);
If you can change storage though, this would be an even better way. jQuery.data works similarly.
There are ways to do this in (almost) one line, with the help of the comma operator [MDN], but it ain't pretty:
var s = storage('foo');
storage('foo', (s.bar = 100, s));
Since you said storage is your function, overloading it seems to be a good way. It would go like this:
function storage(key, prop, val) {
var storage = JSON.parse(localStorage.getItem('storage'));
if(arguments.length === 0) {
return storage;
}
else if (arguments.length === 1) {
// only one argument passed, return the value with key `key`
return storage[key];
}
else if (arguments.length === 2) {
// set `key` to `prop` (`prop` contains the actual value)
storage[key] = prop;
}
else {
// set property `prop` of object in `key` to value `val`
storage[key][prop] = val;
}
localStorage.setItem('storage', JSON.stringify(storage));
}
// Usage
// get `foo`:
var foo = storage('foo');
// set `foo`:
storage('foo', {bar: 42});
// set foo.bar
storage('foo', 'bar', 100);
I hope it gives you some idea.
As storage returns an object, theres a (very) good chance you can do*;
storage('foo').bar = 100;
console.log(storage('foo').bar); // should give 100.
... directly, as objects are "pass by reference"-esque.
* A situation where this would not be the case would be where storage returns a copy of the object stored, rather than the object itself; therefore any updates you make to the returned object will not have an affect on the object in storage.
You can make a function which accepts a function that changes the object:
function changeObject(key, func) {
var obj = storage(key);
func(obj);
storage(key, obj);
}
changeObject('foo', function (s) { s.bar = 100; });

Returning a private variable in JavaScript

I don't know why console.log(Set.current_index) shows 0 instead of 3.
var Set = (function() {
var set = [];
var index = 0;
function contains(set, e) {
for (var i = 0; i < set.length; i++) {
if (set[i] === e) {
return true;
}
}
return false;
}
var add = function(e) {
if (!contains(set, e)) {
set[index++] = e;
}
}
var show = function() {
for (var i = 0; i < set.length; i++) {
console.log(set[i]);
}
}
return {
add: add,
show: show,
current_index: index
};
})();​
Set.add(20);
Set.add(30);
Set.add(40);
Set.show();
console.log(Set.current_index);
As written current_index just gets the initial value of index - it doesn't mirror any changes to that value because that variable is of primitive type.
If you have a 'reference type' (i.e. an object or array) then changes to its contents become visible in any other variable that references the same object. That doesn't happen with primitive types, they're copied "by value" into the new variables, and changes to the original variable don't affect the copy.
You need to make current_index into a function that returns the current value of index, or write it as a getter which allows you to treat .index as a read-only property by invisibly calling a function to return the current value.
For an example of the latter method (which requires ES5, or shims to replicate the functionality) see http://jsfiddle.net/alnitak/WAwUg/, which replaces your current return block with this:
var interface = {
add: add,
show: show
};
Object.defineProperty(interface, 'index', {
get: function() {
return index;
},
enumerable: true
});
return interface;
Javascript always passes by value except when a variable refers to an object. So your initialization of current_index just gets the initial value of index rather than permanently pointing to the variable, so after that initialization, the two variables are on their separate ways therefore incrementing index doesn't increment current_index.

Categories

Resources