I was just doing a small test to see how Javascript would respond to changing a child object's value within a parent object. I wanted to see if the parent referenced the child and would keep up to date with the new values, or if it would only keep the initial state of the child that it was instantiated with.
I have the little module coords.js
var NS = NS || {};
NS.Coords = function(){
var __parentArray = {};
var __childArray = {};
addToParent = function(){
__childArray['value'] = "Initial";
__parentArray['child'] = __childArray;
},
showParent = function(){
console.log("Parent: ",__parentArray);
console.log("Child within parent: ",__parentArray['child']);
},
changeChild = function(){
__childArray['value'] = "Changed";
},
showChild = function(){
console.log("Child: ",__childArray]);
};
return {
addToParent: addToParent,
showParent: showParent,
changeChild: changeChild,
showChild: showChild
};
}();
And in main.js
var NS = NS || {};
// #codekit-prepend "Coords.js"
console.log("=============================");
console.log("Startpoint point");
console.log("=============================");
var coords = NS.Coords;
coords.addToParent();
coords.showChild();
coords.showParent();
console.log("=============================");
console.log("Changed child value");
console.log("=============================");
coords.changeChild();
coords.showChild();
coords.showParent();
If you run this, you see in the console that when shown directly, the child shows the expected "Initial" and then "Changed" values.
The parent, however, always shows the "Changed" value of the child object it references. Even before changeChild() is called. No idea why. Without even changing the value it shows that it's been changed. Am I missing something super simple, or am I misunderstanding what's going on here?
The first problem is you are probably using GOOGLE CHROME, i mean this isn't a problem, but don't forget that if you change a property, the console updates it AUTOMAGICALLY too. (Magical isn't it?)
If you change this console.log("Child within parent: ",__parentArray['child']); to console.log("Child within parent: ",__parentArray['child']['value']); then you can see that your script is working correctly.
Here is the fiddle: http://jsfiddle.net/M4Cd8/
There are some syntax errors in your code, but once those are fixed, the behavior you're describing does not occur. Some browsers' consoles provide an inline reference to objects, that is updated when the object changes. The answer is to observe the value string rather than the whole object:
console.log("Child within parent: ",__parentArray['child'].value);
console.log("Child: ",__childArray.value);
http://jsfiddle.net/pNtJJ/
But to clear up the question you were originally trying to figure out, yes, if you change an object that another object is referencing, then the change is refelcted no matter how you go about observing that, because a reference is precisely that - a link from one object to another.
As a side note, your variable names have the word "array" in them, but those are not arrays that you are working with, but objects. Arrays are typically created with square brackets [1, 2, 3, 4] and contain just values, not key-value pairs.
Related
This is some sort of question from curiossity.
The question is:
How does these Client-side frameworks work, let me explain.
I am working with javascript for more than 5 years. And I don't understand one thing. How do they know when the variable (for example title) variable value changes???.
I would do it like this:
function onTitleChange(title) { //do some stuff }
let title = "This is some title"
let lastTitle = title;
setInterval(() => {
if(lastTitle !== title) {
onTitleChange(title);
lastTitle = title
}
}, 10);
Is this how they all work? Is this how the Vue.js knows when the variable value changes? If not, what sort of magic do they use to know when a variable value changes??
I'm gonna try to explain it in very simple words, step by step:
make a <h2>Hi</h2> element on a simple HTML page
open browser console and store DOM element in a variable:
var h2 = document.getElemntsByTagName('h2')[0];
make two other variables first var obj = {}; and second var text = '';
this is the part that you are looking for:
instead of simply assigning obj.text = text we declare getter setter's for obj.text attribute so that whenever we set new value to obj.text the corresponding DOM element changes too.
Object.defineProperty(obj, 'text', {
get: function () {
return text;
},
set: function (newvalue) {
text = newvalue;
h2.innerHTML = text;
}
});
now go on and change obj.text value : obj.text = "Hello again"
for more information check this page out.
all the codes available at : JSfiddle
There is no magic, Angular for example uses zone.js, I recommend you have a read about it.
Little about React - its not actually listen to changes of the js objects, because of it only call render methods of components, if shouldComponentUpdate() (default it use reference equality of the component state) return true, and check if returned VirtualDOM is equal to the real DOM.
Because of it - its much more faster then Angular, which use many of listeners and equality checkers for watching updates.
I'm currently reading this somewhat outdated but fine React tutorial, and I spent hours looking at this little piece of trouble, which might be more Javascript-related than React-related. At a certain point, the author starts building a small notepad app, and then there's this code:
var onChangeNote = function (id, value) {
var note = _.find(notepad.notes, function (note) {
return note.id === id;
});
if (note) {
note.content = value;
}
onChange();
};
In the app, which can be viewed in full at the forementioned article or on the respective fiddle, we have a list of notes (which by itself is an array assigned to the notes property on a notepad object defined at the top of the script), and the selected one may be changed by the user, all while using React.
What really gets me is that this is the function responsible for changing the content of the note, in the note.content = value; line, but note is a variable that got its value from _.find() (it's the lodash variant, but I already tried replacing it with the vanilla JS array.find() and nothing changed), and yet, changing it appears to be updating the actual array, I found nowhere in the code any other instance of the selected note being changed, and the onChange() function just updates the view layer (therefore it doesn't do anything to the notepad itself), so this has to be it. So is the variable note referencing the actual respective item on the notepad.notes array it got its value from even though Javascript doesn't usually do that?
Maybe I'm missing something really obvious, but I cannot put my finger on it.
Basing from the source we can check that _.find doesn't create a deep copy of the object, it returns the object from the array.
Taken from: https://github.com/lodash/lodash/blob/4.6.0-npm-packages/lodash.find/index.js
function createFind(findIndexFunc) {
return function(collection, predicate, fromIndex) {
var iterable = Object(collection);
if (!isArrayLike(collection)) {
var iteratee = baseIteratee(predicate, 3);
collection = keys(collection);
predicate = function(key) { return iteratee(iterable[key], key, iterable); };
}
var index = findIndexFunc(collection, predicate, fromIndex);
return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
};
}
So yes, it returns the object "reference", and not a clone, so changing a property in it, changes the one in the array.
============
Here's an example regarding your question if javascript is pass by value or reference. Javascript is always pass by value except if the value passed is an object or array. Changing the value of a property to the object will also affect the original one. But changing the whole object will not affect the original one.
var arr = [{a: 1}, {a: 2}];
var x = arr.find(v => v.a === 1);
x.a = 5;
console.log(arr); // you'll see a is 5 here
x = 100; // we changed variable directly (note that x is the object that we extracted from the find function)
console.log(arr); // it's not changed, 5 is still the value
x = arr.find(v => v.a === 5); // let's get that object again
x = {a: 10}; // we replaced it with another object with same property but another value
console.log(arr); // still not changed
Is it possible to keep an object reference without using an holder object in javascript?
Currently when an object gets overridden I sometimes lose the reference to the "current" object state illustrated in the snippet below;
Is there a way to put a "pointer" in an array or not?
EDIT
To the questions asked:
What I have in the objects I have are references to form fields. Some of these are text fields, some of them are textareas, some of them checkboxes.
I wish to keep a map next to the direct referene of what type they are.
basicaly it would be
obj {
this.text1 = createTextField();
this.text1.datepicker();
this.text2 = createTextField();
this.area1 = createArea();
this.check = createCheck();
this.datefields = [this.text1];
this.checkboxes = [this.check];
}
So I can use the datefields/checkboxes array as a checkpoint to validate against which type a field is/should behave.
Currently I use
function datefields() { return [this.text1]; };
But I'd like to know if there's a better way to do this than to intantiate a new array when I need to check it.
I know there is a way with observers to mimic pointer behaviour, and i've fiddled with those and have some good results with that, i'm just curious if there are other ways i'm not aware of.
function myObject() {
this.myvalue = null;
this.arr = [this.myvalue];
}
myObject.prototype.alter = function() {
this.myvalue = "hello";
}
var x = new myObject();
var elem = document.getElementById('results');
function log(message) {
elem.appendChild(document.createTextNode(message));
elem.appendChild(document.createElement('br'));
}
log("x.myvalue = "+x.myvalue);
log("x.arr[0] = "+x.arr[0]);
log("calling alter");
x.alter();
log("x.myvalue = "+x.myvalue);
log("x.arr[0] = "+x.arr[0]);
<div id="results"></div>
Simple answer: Only objects (including all subtypes) are passed by reference in JS. All other simple values are copied.
For a bit more detail I would recommend reading You Don't Know JS: Types & Grammer but specifically the section Value vs Reference in Chapter 2:
In JavaScript, there are no pointers, and references work a bit differently. You cannot have a reference from one JS variable to another variable. That's just not possible.
Quoting further on:
Simple values (aka scalar primitives) are always assigned/passed by value-copy: null, undefined, string, number, boolean, and ES6's symbol.
Compound values -- objects (including arrays, and all boxed object wrappers -- see Chapter 3) and functions -- always create a copy of the reference on assignment or passing.
There are plenty of examples included to show these points. I would highly recommend reading through to get a better understanding of how values/references work in JS.
There is no pointers in Javascript, though you could cheat a little using a wrapper object. Here is a minimal implementation of such an object:
var Wrapper = function (value) {
this.value = value;
};
Wrapper.prototype.valueOf = function () {
return this.value;
};
Then you may use it in place of the original value:
function myObject() {
this.myvalue = new Wrapper(null); // wrapper
this.arr = [this.myvalue];
}
myObject.prototype.alter = function() {
this.myvalue.value = "hello"; // notice the ".value"
}
The rest of your code needs no tweaks.
I want to add data variables to an element before causing a specific behavior, but this may require adding more than one data parameter. How can I accomplish this?
$("#dlg_box").data("r_redirect","index.php").dialog("open");
You can do it like this:
var data = $("#dlg_box").data();
data.r_redirect = "index.php";
data.foo = "bar";
$("#dlg_box").dialog("open");
This was taken from here.
To retrieve your values:
$("#dlg_box").data("r_redirect");
$("#dlg_box").data("foo");
JQuery's data() method also takes an JS Object as a parameter. So you might think of passing {"r_redirect": "index.php", "whatEver": "youWant" ...} etc to pass multiple values match your requirement.
Ultimately, the data() method converts your parameters into an Object. So whether you pass an Object or Key and Value separately should not matter
There are different ways to attach data to a jQuery dialog. If you need to attach multiple Data, I recomend using .data("myData", { /* OBJECT */ }, however you can also use inline string and array data. As far as why yours won't work, with so little code to go on, it could be numerous things. However, I've attached a working example of a Dialog with "params" or data for you to take example from. If you post more of your header code tho, I have a feeling we might find a syntax error or a lack of "doc ready" included. Just some thoughts. Anyway, my example:
jsFiddle
$(function() {
// Set the dialog to not open on load and clear all changes made when closed
$("#dlg").dialog({
autoOpen: false,
modal: true,
close: function(e) {
$(this).children("input").nextAll("p").remove();
}
}) // next i call for my first inner button which will show you how to get "attached" data
.children("#attached").on("click", function(e) {
var dlgData = $("#dlg").data("myData");
$(this).after($("<p />").text(dlgData.data1 + " " + dlgData.data2));
}) // finally, the button that will get the string data that was added in the HTML
.next("#inline").on("click", function(e) {
var dlgData = $("#dlg").data("inline");
$(this).after($("<p />").text(dlgData));
});
// simply open our dialog
$("button").on("click", function(e) {
// HERE data is ATTCHED to our dialog just before opening
$("#dlg").data("myData", { data1: "Hello", data2: "world" }).dialog("open")
});
});
$('#Dialog').data('data1', data1).data('data2', data2).dialog('open');
While Initializing the dialog get the values following:
var data1 = $(this).data('data1');
var data2 = $(this).data('data2');
There are some rules you should be aware of before using this!
ADDING
Adding variables using the object returned from $('.selector').data() works because the data object passes by reference, so anywhere you add a property, it gets added. If you call data() on another element, it gets changed. It is what it is what it is...
Adding an object places a object inside of the data object, as well as "extends the data previously stored with that element." - http://api.jquery.com/data/#entry-longdesc
That means that adding an obj to dataObj becomes
dataObj === { /*previous data*/, obj : { } }
Adding an array does not extend the data previously stored, but doesn't behave the same as a simple value either...
USING
If you have simple values stored, you can place them into variables and do what you want with them without changing the data object.
however
if you are using an object or array to store data on an element, beware!
Just because you store it to a variable does not mean you are not changing data value.
Just because you pass it to a function does not mean you are not changing data values!
It is what it is what it is.. unless it's simple.. then it's just a copy. :p
var data = $("#id").data(); // Get a reference to the data object
data.r_redirect = "index.php"; // Add a string value
data.num = 0; // Add a integer value
data.arr = [0,1,2]; // Add an array
data.obj = { a : "b" }; // Add an object
// but here is where the fun starts!
var r_redirectString = data.r_redirect; // returns "index.php", as expected.. cool
r_redirectString = "changed" // change the value and the compare :
data.r_redirect == r_redirectString // returns false, the values are different
var oArr = data.arr; // Now lets copy this array
oArr.push(3); // and modify it.
data.arr == oArr // should be false? Nope. returns true.
// arrays are passed by reference.
// but..
var oObj = data.obj // what about objects?
oObj["key"] = "value"; // modify the variable and
data.obj["key"] == oObj["key"] // it returns true, too!
So, resources..
What's the best way to store multiple values for jQuery's $.data()?
https://stackoverflow.com/a/5759883/1257652
I got something for the javascript developers amongst us.
I got the following class:
function MyClass(){
this.__defineSetter__("array", function(val){
alert("setter called");
this._array = val;
});
this.__defineGetter__("array", function(){
alert("getter called");
return this._array;
});
this._array = new Array();
};
Now, what happens is that when I execute
var a = new MyClass();
a.array[0] = "MyString";
alert(a.array[0]);
the getter is called twice (which is fine), but the setter is never executed, as the actual array reference does not change, only the content (I guess expected behavior).
However, I'd also need to be "notified" when the array-content is modified. Thus, the call
a.array[0] = "MyString";
should also cause a setter-call (or something similar, important is to receive a notification when the array content has changed.
Anybody into this? How can this be achieved?
As we know,alert(a.array[0]); will only trigger a.array's getter/setter,and a.array[0] equal var p = a.array; p[0] which means what you want is trigger p[0]'s getter/setter,not just p's getter/setter.
So,we can change our mind to this thinking:
add getter/setter to all items of p
so,we can do it like this:
if some like p[6] = 0 is used , which will trigger p's getter/setter , judge if all item of p has getter/setter .if not add it.
if some like p = [2,3,4] is use , simply first set getter/setter to the value.
and the code is: Jsfiddle