I have been using this for a while as a really nice workaround for how to declare static members for my objects but I really don't understand why they become static so I need somebody to explain to me the following behavior.
I have the following declarations:
// Primitive so nothing interesting here
Array.prototype.someMember = "My value is not static";
// Object containing a primitive, now this is the deal
Array.prototype.someOtherMember = {
value: "My value is static"
};
Array.prototype.changeMember = function (newValue) {
// Change the primitive value
this.someMember = newValue;
// Change the primitive value inside the object
this.someOtherMember.value = newValue;
};
And if this is tested the following way:
var arr1 = [], arr2 = [], arr3 = [];
arr1.changeMember('I changed');
alert(arr1.someMember + ', ' + arr2.someMember + ', ' + arr3.someMember);
alert(arr1.someOtherMember.value + ', ' + arr2.someOtherMember.value + ', ' + arr3.someOtherMember.value);
The result is:
I changed, My value is not static, My value is not static
I changed, I changed, I changed
Now if I reassign the entire object in the changeMember method like this:
Array.prototype.changeMember = function (newValue) {
// Change the primitive value
this.someMember = newValue;
// Change the object
this.someOtherMember = { value: newValue };
};
Then someOtherMember is no longer static, but instead the first instance of Array gets its own. I don't understand the behavior because after all I am accessing someOtherMember through this in both cases, so I can't figure out why it's static in the first case and it's not static in the second.
There's no such thing as a static property in JS. What you're seeing is the normal behaviour of prototypal inheritance. The key concept is this: Arrays, and indeed all objects, are sparse An instance doesn't have a property unless it's explicitly assigned to that instance. as long as that doesn't happen, the prototype holds all properties:
The first case
Why are you seeing I changed, My value is not static, My value is not static? Simple: When arrays are instantiated, all instances will appear to have a property called someMember, but in fact, they don't. When you try to access anArray.someMember JS will first scan the instance for that property, if that instance doesn't have that property defined, JS will turn to the prototype and look for someMember there. In your snippet the changeMember method uses this, to refer to the instance that invokes the method, not Object.getPrototypeOf(this).someMember. The latter being the way to change the property on the prototype level. In short:
arr1.changeMember('I changed');
// is the same as doing:
arr1.someMember = 'I changed';
arr1.someOtherMember.value = 'I changed';
The assignment to someMember is a straightforward assignment, setting a property of an instance.
The second case
The second assignment is reassigning a property of an object, that is referenced by Array.prototype.someOtherMember. The Reference to the object literal doesn't change, only the object itself. Just as in any other language: when dealing with references, the object can change as much as it likes, the reference will remain the same (its value might change, but its memory address doesn't). When you redefine the changeMember method to reassign a new Object literal to a property, you're in essence creating the same situation as in case one: straightforward assignment of a new object to a property, that causes JS not to scan the prototype, but merely assign the property on an instance-level. You could use Object.getPrototypeOf(this) or (for older browsers) this.prototype:
Array.prototype.changeMember = function (val)
{
this.someMember = 'I changed At instance level';
Object.getPrototypeOf(this).someMember = 'Reassing prototype property to '+ val;
Object.getPrototypeOf(this).someOtherMember = {value:val};//changes proto only
};
Having said that, if you want something like a static property, you're better of using Object.defineProperty:
Object.defineProperty(Array.prototype,'sortofStatic',{value:'I cannot be changed',writable:false,configurable:false});
More examples of this can be found on MDN
In someOtherMember one instance of Object is shared between all instances of Array.
In the first case you are changing the value of that object, so the value changes for all instances of Array.
In the second case you are changing an object itself. So the instance of an Array will contain another object that the others.
The first case is well understood.
The second case (where someOtherMember isn't static member), you are overwritting the someOtherMember every time you call changeMember (deleting the static member value) therefore ceases to exist the static member.
Array.prototype.someMember = "My value is not static";
// Here `someOtherMember` is simply a property which points to a memory location
// So initially it will point to the same memory location for all instances of `Array`
Array.prototype.someOtherMember = {
value: "My value is static"
};
Array.prototype.changeMember = function (newValue) {
this.someMember = newValue;
// Here we change a value stored in the memory location pointed by `someOtherMember`
// As all instances point to the same memory location this change will reflect in all instances
this.someOtherMember.value = newValue;
};
Array.prototype.changeMember = function (newValue) {
this.someMember = newValue;
// Here for this particular instance we make `someOtherMember` point to a new memory location.
// All instances will point to the old memory location until `changeMember` is invoked.
this.someOtherMember = { value: newValue };
};
Related
Can anyone explain objects and properties to me in Javascript in layman's terms? The Javascript MDN documentation is confusing me.
I am trying to solve a problem in a Javascript tutorial about objects and properties The question is below and my attempt at the code, I think I followed the MDN as far as I can grasp it. Any help would be appreciated!
Question: Add the value of the property argument as a key on the object argument.
The value of the new property should be set to null.
Return object after adding the new property.
Input Example:
{}, 'hello'
{ x: 5 }, 'y'
Output Example:
{ hello: null }
{ x: 5, y: null }
note: the property name is NOT 'property'. The name is the value of the argument called property (a string).
My code:
//NOTE: the function addProperty(object, property) was already in the console and I have to write the solution inside of it.
function addProperty(object, property) {
// code here
let result = addProperty({x: 5}, 'y');
obj[property] = null;
return obj;
}
addProperty(x, 'y');
Layman's explanation:
An object is a collection of properties. You can give a name to the object to keep things organized. For example, lets create a person object.
var person = {};
The object has no properties right now. To further describe the person we can add properties to the object.
person.Name = 'Zim';
person.Age = 29;
person.Gender = 'Male';
person.Weight = 80;
Now this object has some properties to help describe it. A different way to write the same thing:
var person = { Name: 'Zim', Age: 29, Gender: 'Male', Weight: 80 };
If we had to create a program that displays a list of people, storing all of our information inside objects would help keep things organized.
Object properties are sometimes referred to as keys.
Adding properties to objects:
You can add a property to an object using brackets, just like you had in your addProperty function. If you just need it to add a property, set that property to null and return the result it would look something like this:
function addProperty(object, property) {
// code here
object[property] = null;
return object;
}
This would let us create a properies on our object from above by calling
addProperty(person, 'Occupation');
addProperty(person, 'Income');
addProperty(person, 'Height');
I think you're over-thinking it.
First the question:
Add the value of the property argument as a key on the object
argument. The value of the new property should be set to null. Return
object after adding the new property.
emphasis added
So, property will be the KEY (of a key/value pair), and the VALUE will be null of object, which we are also passing in as an argument.
One way to interrogate key/value pairs on a javascript object is through the square-brackets []. So, if you have a key/value pair: { foo: "bar" }, you can get "bar" by: object['foo']. You can also create new key/value pairs like this, so your function can look like:
function addProperty(object, property) {
object[property] = null;
return object;
}
var obj = {};
obj = addProperty(obj, "hello");
console.log(obj);
console.log(addProperty({x: 5}, 'y'));
What our function is doing is taking the object passed into it (as an argument), creating a new KEY with our property argument, and setting its VALUE to null, and simply returning the object.
*Side note -
Be careful, the code you have posted will create an endless recursive loop, as you keep calling the same function with no way to break out of it.
This particular example is simple:
var one={};
var two={x:5};
function addProperty(object property){
obj[property]=null;
return obj;
}
addProperty(one, 'hello');
addProperty(two, 'y');
Objects in javascript are really flexible, their properties can be added or removed, even when set from the very beginning.
If you take:
var x={};
x will be an object with nothing in it, but if instead of that you write:
var x={
inner:'b'
};
x will be an object with a property called inner which value is 'b', now, if we want to access that property we could do something like this:
var valueOfInner=x.inner;
or
var valueOfInner=x['inner'];
The same if we want to change the value of that property:
x.inner=8;
or
x['inner']=8;
Now, you'll notice that when we use x.['inner'], we could very well use instead:
var propertyName='inner';
x[propertyName]=8;
So you can access and modify a property of an object without actually knowing exactly which property you're manipulating.
Finally, if you're (willing or by accident) trying to set the value of a property that doesn't exists, the property will be automatically be created, for example:
x['blah']=456;
Will create the property blah even when it wasn't defined at first.
Edit: yes, you can define the object and define its properties later:
var x={};
//more code or something
x['y']=777;//now x has a y property with the value 777
a property is a key:
let obj = {
name: 'Jordan',
age: 15
}
obj.name is a property, same goes for obj.age.
var number = 12345;
var obj = {};
do something like
obj.look ((((((some operator or function to get a reference of number ))))) number
anyway, let's think now obj has a reference to number.
so I would like to wanna do it this way.
obj.look = 'abc';
console.log (number); // hopefully 'abc'
i would like it's gonna be:
not method ( like obj.look() )
but can refer in property ( like obj.look )
all I'm asking is how to get a reference of primitive value.
I believe there must be some way. please help me out.
You can use Object.defineProperty:
var number = 12345;
var obj = {};
Object.defineProperty(obj, 'look', {
get() {
return number;
},
set(value) {
number = value;
}
});
document.write(obj.look + '<br>');
obj.look = 'abc';
document.write(number);
You can have an object, copy that reference to another variable, mutate the object and since both variables hold a reference to the same object any changes done from one variable will also be accessible from the other variable.
Primitive values can be wrapped into objects (new Object) but they can not be referenced otherwise. String objects are immutable (all properties are non-configurable, non-writable) and Number objects values are not accessible either. So you can't change the value of either object.
That means when you do:
obj.look = 'abc';
You are wiping the old reference and setting up a new string there (object or primitive). number will still hold the old reference and will remain unchanged.
So, no. I don't think this is possible to do in Javascript.
Let's say there is a simple object literal which name will never change:
var car = {
wheels : 4,
construct : function() {
var that = this;
setTimeout(function() {
console.log(that.wheels);
console.log(car.wheels);
}, 500);
}
};
My question is: Which way is better? Referencing by the object's name or creating a new variable (which may take some time and memory and probbaly must be done in multiple functions)?
Within the object, you should always refer to the object via this (or a copy of it, e.g. that, if required) to prevent the following breakage:
var car = ...
// do stuff
car = undefined; // or anything else, perhaps by a code hacker in the JS console
// class is now broken
You should treat the variable name that happens to have been given to your object on the outside as unknown to you, and subject to change.
Someone else might call it something else, there might be multiple names, the name might suddenly point at some other object altogether. Such variables are for the benefit of the "owners" of references to the object, and not for the object itself.
Recently I started learning about object-oriented programming in JavaScript. What I understood, is that when referencing to variables, we in fact reference not to their actual values, but locations in the memory. That's why all those "return this" methods that are supposed to copy instances don't work.
So, example code:
//An example object with a simple property and
//failing "copy" function.
function MyObject()
{
this.myProperty = 123;
this.copy = function() { return this; };
}
var iOne = new MyObject();
var iTwo = iOne.copy();
iTwo.myProperty = 321;
Now "myProperty" property of both iOne and iTwo equals 321, because "copy" method returned a reference, instead of a value. This behavior is expected, and everything is okay.
Now, I tried doing the same with a native object type, Number. Let's create an instance of it, in a more object-oriented programmer-friendly way:
var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method
iOne = 321;
And now, something terrible happened. iOne equals 321, but iTwo kept its value and is still equal to 123.
I have no idea what is this behavior caused by. Maybe Number is some kind of "special"? Maybe the decimal number associated with it is something more than a property? Or maybe it's just supposed to make life of inexperienced programmers easier? The last option is related to operators. If anyone know something about it, please don't let my way of understanding JavaScript fall apart.
Objects, Arrays and Strings are assigned by reference (not by copy). All other types are effectively copies when assigned (e.g. they make a new variable that has nothing to do with the old one).
Strings are a special case because they are immutable so when you change a string, it always creates a new string so it behaves more like it makes a copy even though the previous assignment was a reference.
Assigning:
iOne = 321;
Is replacing the value of iOne with a simple primitive numeric type so it will have no effect on any other variable.
var iOne = new Number(123);
var iTwo = iOne; //Equals "iTwo = iOne.copy()", except there isn't a copy method
iOne = 321;
You're overwriting the object reference held by the iOne variable, with a distinct primitive number.
The objects are held as references, but they are not pointers that can be directly dereferenced, so you can't replace the data held in that memory location. You can only mutate it (if the object is mutable).
Specifically, the Number object wrapper is not mutable, or at least the primitive value it holds can't be replaced. You can only replace the entire object.
iOne = 321;
This code did what is expected, you assigned 321 to the variable iOne, overwriting what it was referencing to originally.
There's no real difference in behaviour between "native types" and objects in Javascript (except that native types are immutable).
In your second example you're simply changing what variable iOne is pointing to, why should it change what another independent iTwo variable is pointing to?
In the first case instead you have two variables pointing to the same object and if you use one variable to mutate the object and you can observe the change also using the other variable (obvious... it's pointing to the same object).
In Javascript you can imagine that everything is always by reference and never by value (copy). If you want to copy something you need to do it explicitly... for arrays you can use x.slice() to make a shallow copy of x; for objects there's no primitive function for doing the same so you must call the constructor.
A common OOP pattern is to have a member function .clone() that returns a copy so who needs the copy doesn't need to know how to make a copy of every class.
function P2d(x, y) {
this.x = x;
this.y = y;
}
P2d.prototype.clone = function() {
return new P2d(this.x, this.y);
}
Another possibility specific to the protoype model of Javascript and that can be useful in some cases is to create a separate object that will appear like a shallow copy that can be mutated without affecting the original but that is instead referencing the original object when reading:
function fakeCopy(x) {
function f() { }
f.prototype = x;
return new f;
}
p = new P2d(10, 20);
q = fakeCopy(p);
console.log(q.x); // Displays 10
q.y = 30;
console.log(q.y); // Displays 30
console.log(p.y); // Displays 20 -- original not changed
p.x = 99;
console.log(q.x); // Displays 99 (!)
This happens because Javascript objects have a "prototype chain" that is searched when accessing a member for reading. q is created as an empty object that with p as its prototype so when looking for an attribute (for reading) it will go searching inside p if something is not found inside q. When writing however an attribute will be set inside q, not affecting p and from that point on the value present in q will be returned instead of having to go up in the prototype chain.
should a getter of a private property return a reference to the property or the value of the property (or a clone if the property is an object).
If the property has a primitive type (string, number, boolean) or a reference to an immutable object (String, Number), the answer is that you return the value/reference directly, since there's nothing the caller can do with it that will change your object's state.
If the property has a reference type to a mutable object (an array, an Object, a Date, lots and lots of other things), it depends on whether you want the caller to be able to change the properties on your object's copy of it. If you do, return the reference. If not, clone it.
For example, let's assume an object with a private array and some operations you can perform on that array:
var obj = (function() {
var o = {};
var theArray = [];
o.addItem = function(item) {
theArray.push(item);
};
o.getItemAt = function(index) {
return theArray[index];
};
return o;
})();
(Note that this is not meant to be a glorious example of object design. :-) )
Now you want to add a getArray operation. You can do that in two ways:
You can return a reference to the array:
o.getArray = function() {
return theArray;
};
...which means the caller can do all sorts of things to it (like remove items from it, an operation the object didn't previously support) that will change your object's state (because you've given them direct access to the formerly-private array).
Or you can return a clone of the array:
o.getArray = function() {
return theArray.slice(0);
};
...which means the caller can't do anything to the private array, they can only operate on the copy of it you gave them.
This depends whether the private property is immutable or not. If it's immutable, it can't be changed and there is no harm in returning it. If however it is mutable, then returning it will break the OOP principle of information hiding.
If it's a primitive immutable int/number, then returning the private property is fine since it achieves two things:
You hide the private property and provide an interface, and any changes to the implementation of the private property will not break the interface as easily
The returned object (in this case a value) can't be changed by the caller because it's a primitive type and is passed by value
If however, you return a mutable Array from a method and your language passes objects by reference, then you must accept the fact that the caller may decide to change this internal object - which could very easily lead to your internal code breaking.
For these reasons, if you plan to return a copy to a mutable object, return a copy and not the underlying object itself.
Generally wherever the property is to be used should not be able to change the property value. This satisfies the important principle of the OOP i.e Encapsulation.