Can I do something in Javascript that will automaticaly transform this:
var x = 0;
into the following (but only in memory-- I don't want to change the "view source"):
var x = {Value: 0};
For exemple:
var x = { Value: 0 };
function a(x) //This function doesn't work if the parameter is not an object, and it would be better if I didn't have to write { Value: 0 }
{
x.Value++;
}
a(x);
alert(x.Value);
The question lacks context and details, but maybe do it like this:
function transform(x) {
return { Value : x };
}
and then
x = transform(x);
have a look at watch and Object.watch() for all browsers?, e.g.:
var a = function (x) {
x.value++;
};
var myVariables = {};
myVariables.watch("x", function (prop, oldval, newval) {
if (typeof newval !== 'object') {
return {"value": newval};
};
});
myVariables.x = 0;
a(myVariables.x);
alert(myVariables.x.value);
Related
i've a problem in js. I've a function for example:
function robePersos() {
var persos = {"player" : "data"};
return persos;
}
and then i've another function which call robePersos() like this:
function test() {
var d = robePersos();
for(var k in d) {
console.log(k)
}
}
But nothing happens. Why ?
function robePersos() {
var persos = {
"player": "data"
};
return persos;
}
function test() {
var d = robePersos();
for (var k in d) {
console.log(k)
}
}
test();
EDIT
The first snippet works. So, here is my real function:
function robePersos() {
var persos = {};
$.get({
url : 'url',
success : function(data) {
var text = $(data).find("div[menu='perso'] a"); //.clone().children().remove().end().text();
$(text).each(function(){
perso_name = $(this).text();
perso_link = $(this).attr('href');
persos[perso_name] = perso_link;
});
}
});
for(var k in persos) {
console.log(persos[k]);
}
}
robePersos();
If I replace the loop by only console.log(persos) it works but the loop return nothing. Why ?
If you want to print both Key and Value, use the following small change in your code. Your code is printing just the keys.
function robePersos() {
var persos = {
"player": "data",
"anotherPlayer": "anotherData"
};
return persos;
}
function test() {
var d = robePersos();
for (var k in d) {
console.log(k, "-" ,d[k]); // change made here. It prints both.
}
}
test();
Try whith Object.keys()
function test() {
var d = Object.keys(robePersos());
for (var k in d) {
console.log(k, "-" ,d[k]); // change made here. It prints both.
}
}
Object.keys returns an array whose elements are strings corresponding to the enumerable properties found directly in the object. The order of the properties is the same as that provided when manually iterating over the properties of the object.
https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Object/keys
The below code (also in Plunker) is adapted from this SO post.
I am trying to implement the same logic, only add the uniqueId function in a special property of the Object.prototype to keep things clear.
The below code works when run using nodejs (also the HTML-ized Plunker example works as well). I.e. the console prints three unique object identifiers: 0, 1 and 2.
However, when the test switch variable is set to 1, console prints 0, 0 and 0.
What should I do to add the uniqueId function in the foo namespace?
function addUniqueId1(){
if (Object.prototype.foo===undefined) {
Object.prototype.foo = {};
console.log('foo "namespace" added');
}
if (Object.prototype.foo.uniqueId===undefined) {
var i = 0;
Object.prototype.foo.uniqueId = function() {
console.log('uniqueId function called');
if (this.___uniqueId === undefined) {
this.___uniqueId = i++;
}
return this.___uniqueId;
};
console.log('function defined');
}
}
function addUniqueId2() {
if (Object.prototype.uniqueId === undefined) {
var i = 0;
Object.prototype.uniqueId = function() {
if (this.___uniqueId === undefined) {
this.___uniqueId = i++;
}
return this.___uniqueId;
};
};
};
var test=2; // if you set this to 1 it stops working
if (test==1)
addUniqueId1();
else
addUniqueId2();
var xs = [{}, {}, {}];
for (var i = 0 ; i < xs.length ; i++) {
if (test==1)
console.log('object id is: ['+xs[i].foo.uniqueId()+']');
else
console.log('object id is: ['+xs[i].uniqueId()+']');
}
You need to define foo with a getter, to allow it to access this for use within the hash of sub-method(s):
function defineFoo() {
if (Object.prototype.foo) return;
var i = 0;
Object.defineProperty(Object.prototype, 'foo', {
get: function() {
var self = this;
return {
uniqueId: function() {
return self.__uniqueId = self.uniqueId || ++i;
}
};
}
});
}
Now
defineFoo();
obj.foo.uniqueId()
If you prefer, an alternative to using self:
Object.defineProperty(Object.prototype, 'foo', {
get: function() {
return {
uniqueId: function() {
return this.__uniqueId = this.uniqueId || ++i;
}.bind(this)
};
}
});
I am currently trying to observe any changes to a given object including all of it's elements.
The following code only fires when an object[x] is updates, but not if individually updating object[x]'s elements such as object[x][y]
<script>
var elem = document.getElementById("test1");
var log = function(x) {
elem.innerHTML += x + "<br/><br/><br/>";
};
var a = [{a:1,b:2},
{a:2,b:5}
];
var source = Rx.Observable
.ofObjectChanges(a)
.map(function(x) {
return JSON.stringify(x);
});
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1];
</script>
This code runs and fires correctly.
however. if I instead to this
a[0]['a'] = 3;
Then nothing happens.
EDIT
A better way to phrase this, how can I observe changes from an array of objects?
If you want only the nested object changes:
var source = rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
});
If you also want changes like a[0] = a[1]:
var source = rx.Observable.merge(
rx.Observable.ofArrayChanges(a),
rx.Observable.from(a).flatMap(function(item) {
return rx.Observable.ofObjectChanges(item);
})
);
The flatMap or selectMany (they are the same function) will allow you to iterate over a value and execute a function that returns an Observable. The values from all these Observables are "flattened" onto a new stream that is returned.
http://reactivex.io/documentation/operators/flatmap.html
Perhaps something like this by merging two Observables (one for the array and the other observing the elements of the array):
var a = [
{a:1,b:2},
{a:2,b:5}
];
var source1 = Rx.Observable.ofArrayChanges(a).map(function(x) {
return JSON.stringify(x);
});
var source2 = Rx.Observable
.fromArray(a.map(function(o, i) { return [o, i]; }))
.flatMap(function(oi) {
return Rx.Observable.ofObjectChanges(oi[0])
.map(function(x) {
var y = {
type: x.type,
object: x.object,
name: x.name,
oldValue: x.oldValue,
arrayIndex: oi[1] // pass the index of the member that changed
};
return JSON.stringify(y);
});
})
source = source1.merge(source2)
var subscription = source.subscribe(
function (x) {log(x);},
function (err) {log(err);},
function () {log('Completed');}
);
a[0] = a[1]
a[1]['b'] = 7
Thanks to #electrichead here we're not using concatMap because the sources that we made by ofObjectChanges and ofArrayChanges never complete.
Here's a working example of Rx.Observable.ofNestedObjectChanges simple implementation, you can get the gist of it and implement you own.
http://jsbin.com/wekote/edit?js,console
Rx.Observable.ofNestedObjectChanges = function(obj) {
if (obj == null) { throw new TypeError('object must not be null or undefined.'); }
if (typeof Object.observe !== 'function' && typeof Object.unobserve !== 'function') { throw new TypeError('Object.observe is not supported on your platform') }
return new Rx.AnonymousObservable(function(observer) {
function observerFn(changes) {
for(var i = 0, len = changes.length; i < len; i++) {
observer.onNext(changes[i]);
}
}
Object.observe(obj, observerFn);
//Recursive observers hooks - same observerFn
traverseObjectTree(obj, observerFn);
function traverseObjectTree(element, observerFn){
for(var i=0;i<Object.keys(element).length;i++){
var myObj = element[Object.keys(element)[i]];
if(typeof myObj === "object"){
Object.observe(myObj, observerFn);
traverseObjectTree(myObj,observerFn);
}
}
}
return function () {
Object.unobserve(obj, observerFn);
};
});
};
//Test
var json = {
element : {
name : "Yocto",
job : {
title: "Designer"
}
},
element1: {
name : "Mokto"
}
};
setTimeout(function(){
json.element.job.title = "A Great Designer";
},3000);
var source = Rx.Observable.ofNestedObjectChanges(json);
var subscription = source.subscribe(
function (x) {
console.log(x);
},
function (err) {
console.log('Error: %s', err);
},
function () {
console.log('Completed');
});
json.element.name = "Candy Joe";
I'm testing the observable pattern in javascript. My callbacks in the array never seem to execute. What is wrong with my syntax?
<script type="text/javascript">
var Book = function (value) {
var onChanging = [];
this.name = function () {
for (var i = 0; i < onChanging.length; i++) {
onChanging[i]();
}
return value;
}
this.addTest = function (fn) {
onChanging.push(fn);
}
}
var b = new Book(13);
b.addTest(function () { console.log("executing"); return true; });
b.name = 15;
</script>
From your code above it looks like you need to call your function name instead of assigning a value something like:
var b = new Book(13);
b.addTest(function () { console.log("executing"); return true; });
b.name(); //<-- Before b.name = 15
Setting b.name = 15 doesn't execute the function, it just overwrites the value of b.name.
You could use getters and setters to react to a changing value. See John Resig's blog post or the MDN reference
I edited your code to use them:
var Book = function (value) {
this.onChanging = [];
this._name = "";
}
Book.prototype = {
addTest: function (fn) {
this.onChanging.push(fn);
},
get name() {
return this._name;
},
set name(val) {
for (var i = 0; i < this.onChanging.length; i++) {
this.onChanging[i](val);
}
this._name = val;
}
};
var b = new Book(13);
b.addTest(function (val) {
console.log("executing", val);
return true;
});
b.name = 15;
b.name = 17;
working demo.
You can also make a more generic solution that can work for all your properties without having to define the getters and setters, a lot of frameworks use this approach.
Book = function () {
this._events = [];
this._rawdata = {};
}
Book.prototype = {
bind: function (fn) {
this._events.push(fn);
},
// pass the property, and it returns its value, pass the value and it sets it!
attr: function (property, val) {
if (typeof val === "undefined") return this._rawdata[property];
this._rawdata[property] = val;
for (var i = 0; i < this._events.length; i++)
// we pass out the val and the property
this._events[i](val, property);
}
};
b = new Book();
b.bind(function (val) {
console.log("executing", val);
return true;
});
b.attr("name","The Hobbit");
b.attr("SKU" ,1700109393901);
console.log(b.attr("name")); // --> The Hobbit
http://jsfiddle.net/wv4ch6as/
Of course you would want to change the binder so that you can bind onto properties not one bind for all properties, but I think this gets the idea.
I created an ObservablePropertyList which is supposed to execute a callback when a property changes. The implementation is:
function ObservablePropertyList(nameCallbackCollection) {
var propertyList = {};
for (var index in nameCallbackCollection) {
var private_value = {};
propertyList["get_" + index] = function () { return private_value; }
propertyList["set_" + index] = function (value) {
// Set the value
private_value = value;
// Invoke the callback
nameCallbackCollection[index](value);
}
}
return propertyList;
}
And here's a quick test demonstration:
var boundProperties = BoundPropertyList({
TheTime: function (value) {
$('#thetime').text(value);
},
TheDate: function (value) {
$('#thedate').text(value);
}
});
var number = 0;
setInterval(function () {
boundProperties.set_TheTime(new Date());
boundProperties.set_TheDate(number++);
}, 500);
For some reason though, the properties are not being assigned correctly or something. That is, calling set_TheTime for some reason executes the callback for set_TheDate, almost as though it were binding everything to only the last item in the list. I can't for the life of me figure out what I'm doing wrong.
When using loops like that you need to wrap it in an enclosure
function ObservablePropertyList(nameCallbackCollection) {
var propertyList = {};
for (var index in nameCallbackCollection) {
(function(target){
var private_value = {};
propertyList["get_" + index] = function () { return private_value; }
propertyList["set_" + index] = function (value) {
// Set the value
private_value = value;
// Invoke the callback
target(value);
}
})(nameCallbackCollection[index]);
}
return propertyList;
}
You need to create a closure in order for each iteration of the for loop to have its own private_variable object. Otherwise, each iteration just overwrites the previous (since private_variable is hoisted to the top of its scope). I'd set it up like this:
var ObservablePropertyList = (function () {
"use strict";
var handleAccess = function (propList, key, callback) {
var privValue = {};
propList["get_" + key] = function () {
return privValue;
};
propList["set_" + key] = function (value) {
// Set the value
privValue = value;
// Invoke the callback
callback(value);
};
};
return function (coll) {
var propertyList = {}, index;
for (index in coll) {
handleAccess(propertyList, index, coll[index]);
}
return propertyList;
};
}());
var boundProperties = ObservablePropertyList({
TheTime: function (value) {
$('#thetime').text(value);
},
TheDate: function (value) {
$('#thedate').text(value);
}
}), number = 0;
setInterval(function () {
boundProperties.set_TheTime(new Date());
boundProperties.set_TheDate(number++);
}, 500);
DEMO: http://jsfiddle.net/PXHDT/