So I was playing around the other day just to see exactly how mass assignment works in JavaScript.
First I tried this example in the console:
a = b = {};
a.foo = 'bar';
console.log(b.foo);
The result was "bar" being displayed in an alert. That is fair enough, a and b are really just aliases to the same object. Then I thought, how could I make this example simpler.
a = b = 'foo';
a = 'bar';
console.log(b);
That is pretty much the same thing, isn't it? Well this time, it returns foo not bar as I would expect from the behaviour of the first example.
Why does this happen?
N.B. This example could be simplified even more with the following code:
a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);
a = 'foo';
b = a;
a = 'bar';
console.log(b);
(I suspect that JavaScript treats primitives such as strings and integers differently to hashes. Hashes return a pointer while "core" primitives return a copy of themselves)
In the first example, you are setting a property of an existing object. In the second example, you are assigning a brand new object.
a = b = {};
a and b are now pointers to the same object. So when you do:
a.foo = 'bar';
It sets b.foo as well since a and b point to the same object.
However!
If you do this instead:
a = 'bar';
you are saying that a points to a different object now. This has no effect on what a pointed to before.
In JavaScript, assigning a variable and assigning a property are 2 different operations. It's best to think of variables as pointers to objects, and when you assign directly to a variable, you are not modifying any objects, merely repointing your variable to a different object.
But assigning a property, like a.foo, will modify the object that a points to. This, of course, also modifies all other references that point to this object simply because they all point to the same object.
Your question has already been satisfyingly answered by Squeegy - it has nothing to do with objects vs. primitives, but with reassignment of variables vs. setting properties in the same referenced object.
There seems to be a lot of confusion about JavaScript types in the answers and comments, so here's a small introduction to JavaScript's type system:
In JavaScript, there are two fundamentally different kinds of values: primitives and objects (and there is no thing like a 'hash').
Strings, numbers and booleans as well as null and undefined are primitives, objects are everything which can have properties. Even arrays and functions are regular objects and therefore can hold arbitrary properties. They just differ in the internal [[Class]] property (functions additionally have a property called [[Call]] and [[Construct]], but hey, that's details).
The reason that primitive values may behave like objects is because of autoboxing, but the primitives themselves can't hold any properties.
Here is an example:
var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);
This will output undefined: a holds a primitive value, which gets promoted to an object when assigning the property foo. But this new object is immediately discarded, so the value of foo is lost.
Think of it like this:
var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created
You're more or less correct except that what you're referring to as a "hash" is actually just shorthand syntax for an Object.
In the first example, a and b both refer to the same object. In the second example, you change a to refer to something else.
here is my version of the answer:
obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"
// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"
// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory
You are setting a to point to a new string object, while b keeps pointing to the old string object.
In the first case you change some property of the object contained in the variable, in the second case you assign a new value to the variable. That are fundamentally different things. The variables a and b are not somehow magically linked by the first assignment, they just contain the same object. That's also the case in the second example, until you assign a new value to the b variable.
The difference is between simple types and objects.
Anything that's an object (like an array or a function) is passed by reference.
Anything that's a simple type (like a string or a number) is copied.
I always have a copyArray function handy so I can be sure I'm not creating a bunch of aliases to the same array.
Related
If i am not wrong, the pointer that the variable name is using cannot change in memory, but the thing the variable points to might change.
let name = "google";
const foo = name;
console.log(foo); //prints google
name = "yahoo";
console.log(foo); //prints google again instead of yahoo
Should it not print yahoo, as the variable name's value has been changed.. Can anyone explain me this.
Another example, where its changes...
const foo = [];
foo.push("test");
console.log(foo); // outputs ["test"]
I am getting confused here, can anyone explain me this.
Your first example uses an immutable string and the second uses a mutable object.
JS strings are immutable after being declared or created, so foo is not a reference to name's value, it points to the string. You're not changing the strings, you're pointing to a different one.
The array variable points to the object and continues pointing to the same object after it's been mutated. You're still pointing to the same object, however, since const is not deep.
This also highlights a common misunderstanding with JS' const, which functions more like Java's final than C++'s const. You are declaring a variable as const, not an instance of the object, so it only prevents you from reassigning to the variable, it does not prevent you from mutating the underlying object.
From MDN:
The const declaration creates a read-only reference to a value. It does not mean the value it holds is immutable, just that the variable identifier cannot be reassigned.
This means that you can change the value assigned to a const, say by changing array or object, but cannot assign a new value to the const variable. E.g.:
Valid:
const a = [];
a.push(5);
console.log(a);
const b = {};
b['foo'] = 'bar';
console.log(b);
Invalid: This will throw error
const a = [];
a = [5];
console.log(a);
const b = {};
b = {'foo': 'bar'};
console.log(b);
ECMAScript 5
has defined a very common factory pattern for construction and inheritance, called Object.create(). I am simply passing in the object to
inherit from, and getting back a new object all properly wired up.
Consider the snippet:
var a = {
phrase: "Hello",
say: function(){ alert(this.phrase); }
};
var b = Object.create(a);
Is my understanding correct as reflected below?
When b is first created, it has a prototype-inherited property called
phrase, meaning that the property is not on the instance (b.phrase),
but actually on the prototype (b.prototype.phrase). If I were to
read the value of b.phrase at that point, JavaScript would implicitly
look it up, find it on the prototype, and return it to me.
b.say(); // alerts Hello
However, when I make the assignment of a new value to b.phrase, I see it doesn’t affects the inherited
b.prototype.phrase but have instead set (overridden) an instance property b.phrase that takes precedence over the inherited
b.prototype.phrase on future property accesses. Why?
b.phrase = "World";
a.say(); // alerts Hello >> This hasn't changed. Why?
b.say(); // alerts World
This happens because of the prototype chain. JavaScript runtime looks for own object's properties first, and if nothing is found, it looks for the property on its prototype, and so on.
In the other hand, you're not overriding a property, but you are just adding one to the whole object and it hides the prototype's one, and again, this happens because of how prototype chain works.
This is the way javascript property lookup/assignment work. If you want to update prototype property, you can create object inside prototype object.
var proto = {
fields: {
phrase: 'Hello'
},
say: function () { console.log(this.fields.phrase) }
};
var a = Object.create(proto);
a.fields.phrase = 'World';
proto.say();
a.say();
So, what is going on here?
a.fields.phrase = 'World' is equal to
var tmp = a.fields;
tmp.phrase = 'World';
a.fields === a.__proto__.fields // true
That is why property was updated in prototype.
In your example you just assign value to object and js engine does what you want – assigns value "World" with key "phrase" to object a, nothing weird
More info about how objects work in js
First take this statement
var b = Object.create(a);
This statement creates a new object, newObj(for instance). Now following things happen:
b points to newObj.
newObj is linked to a's object via [[prototype]] reference.
Till now newObj does not have any methods or properties.
Take b.Say() -- b will point to newObj,so first it tries to check whether newObj has Say() method, it doesn't have so it tries to delegate via [[prototype]] chain. As newObj's [[prototype]] link points to a's object. It tries to check whether Say() is present in a's object.So it finds the method there & execute it with the context of newObj. It prints 'Hello'
Take b.Phrase='world'
Here you are assigning phrase property to newObj object (pointed by 'b'). So from next time if you are trying to do b.Phrase it will not traverse up the [[Prototype]] chain (i.e to a's object), instead it will find the value in newObj itself.
Final b.Say()
As newObj does not have Say() method, it will traverse up the [[prototype]] chain, finds the method, & executes it in the context of newObj. And as newObj has phrase property so this.phrase returns 'world'
I'm reading that in JavaScript, a common point of confusion arises because variables of primitives are passed by value, and variables of objects are passed by reference, while in function arguments, both primitives and references are passed by value.
In the course of my tinkering, I've made up the following code, but am having trouble wrapping my head around it.
> function setName2(obj) {
... obj.name="matt";
... obj = new Object();
... obj.name="obama";
... }
If I set
var person = new Object();
person.name = "michelle";
Then run
> setName2(person);
I get
> person.name;
'matt'
Which makes sense because the new object created is a pointer to a local object, hence not affecting the property of the global 'person'.
However, what if I first set
var obj = new Object();
obj.name = "michelle";
Then run
> setName2(obj);
?
I get the same outcome. Does this mean that the compiler recognizes the two variables of the same name (obj global and obj local) as references to different locations within the heap, each having some different pointer association, or is there a different explanation for this phenomenon?
JavaScript does not have pass-by-reference; everything is passed by value. However, some values are themselves references to objects. This distinction (pass-by-reference vs. is-a-reference) leads to much confusion.
An example to clear things up:
function f(o) { ... }
var obj = { foo: 42 };
f(obj);
No matter what you do in f, obj will always refer to the same object because the argument is not passed by reference. However the value obj, which is copied into o, is itself a reference to an object. This is why any property changes made to o inside the function will be visible on obj after it returns.
while in function arguments, both primitives and references are passed by value.
This is not true. There is nothing special about function arguments.
function setName2(obj) {
This accepts a reference to an object as an argument.
obj.name="matt";
This modifies the name property of the object that reference points to.
obj = new Object();
This replaces the reference to the original object with a reference to a new object.
obj.name="obama";
This modifies the name property of the new object. The original object is unchanged.
The confusion comes from the fact that "passed by reference" is misinterpreted by people or used in a wrong sense.
Parameters are passed by value. This means that changing the value inside the method doesn't change the original value.
In case of primitives, the value of a primitive is its value.
In case of objects, the value of an object is a reference to it. You can access and change object's content but you can't change the value of the reference itself.
In other programming languages, like C++ or C#, "passing by reference" means that you pass:
- a reference to a primitive type
- a reference to a reference to an object
In such case, not only the content of an object can be changed but also a reference itself can be changed.
There is NO passing by reference in Javascript.
Javascript uses pass-by-value.
The confusion is that objects are hold by reference variables (kind of pointers). In fact most common languages (java, javascript, etc.) do not have a real pass-by-reference behaviour. Another way to understand this could be pass-reference-by-value, although, formally, there is not such a thing.
That means when you pass an object as a parameter, you are actually passing a reference to the object by-value.
function setName2(obj) {
...
}
setName2(person);
here the contents of person (a reference, or "pointer" if you like) is copied by-value to a new local variable: obj.
obj and person are different variables that hold a reference to the same object.
So, doing obj = new Object(); makes obj to point to the new object. But person is unaffected since it is still a completely different variable.
I don't know where you read that, but it's absolutely not true. Objects are passed by reference, full stop. Whether or not it's a function parameter is completely irrelevant.
I don't understand why the following is happening:
function Class() {}
Class.prototype.foo = {a: 1};
Class.prototype.bar = 1;
x = new Class();
y = new Class();
y.foo.a = 2;
y.bar = 2;
x.foo.a; //2
x.bar; //1
When I set y.foo.a to 2, it seems to be performing the same operation as y.constructor.prototype.foo.a = 2. Why would this be, given that y.bar = 2 doesn't affect y.constructor.prototype.bar?
You are mixing prototype properties with properties that are local to the object instance. Using y.bar = 2, you are assigning an instance property (bar) to the instance y. In interpreting a property (bar) is first looked up within the instance itself. If it isn't found there, the lookup continues in the instances prototype. Now you assigned bar to y, so y.bar = 2, but instance x has no knowledge of it, so for x the lookup continues using its prototype (that's prototype.bar, still with value 1).
For y.foo.a, there is no instance property foo in y, so it's looked up in its prototype. There it's found, and the property a is assigned the new value. Because you are thereby changing a value of the Class.prototype property foo, it's also represented in x.
If you want to change bar in its prototype (so originating from instance y), you'll have to use the prototype of y's constructor (which is Class):
y.constructor.prototype.bar = 2;
May mr Douglas Crockford can clarify things for you (jump to around minute 23 in the video for prototypal inheritance)?
You are reading y.foo but assigning to y.bar. These are different operations with different semantics. Setting y.foo.bar must first read y.foo: it looks for the value of foo in y, fails to find it, then looks in y's prototype, discovers an object, and only then modifies that object. assigning y.bar simply looks up y and then modifies it. x.bar and y.bar then denote different objects, while x.foo and and y.foo denote the same object.
Reading an attribute does search in the prototype object, if it is not defined in the main object.
Setting an attribute does ever set it to the main object and NEVER to the prototype object.
Because of this, it is ever a bad idea to define something other than functions, or values that will never change in prototype objects.
To really understand what happens, I recommend you to experiment with the
__proto__
attribute. I know, not all browsers do support it, but it really helps you understand how prototyping in JavaScript works.
Why is it that I can do the following in javascript:
function a() {};
a.foo = 30;
Specifically, why can I set a property on the function a? After all, I cannot do this:
var a = 20;
a.foo = 30;
You really can't do this because it's a syntax error
function a = function() {};
I suppose you simply want to say:
function a() {}
Anyway. The reason that you cannot get a property out of a number, is that it is not a real Object.
a = 20;
a.foo = 30; // this works
alert(a.foo); // this alerts nothing
Believe it or not, the same goes for strings:
a = "ohai";
a.foo = 30; // this works
alert(a.foo); // this alerts nothing
However if it's String object, then it works as expected:
a = new String("ohai");
a.foo = 30; // this works
alert(a.foo); // this alerts 30
Or if it's an Number object. You get the point.
String and number literals are not objects in Javascript. That's the reason.
In JavaScript the dot (.) operator expects it's left value to be an object. And in JavaScript, functions are objects.
Basically, in JavaScript, there are four main datatypes:
Number
String
Boolean
Object
Objects encompasses functions, arrays, date objects, and (for lack of a better word) regular objects. The function object is unique in that it contains executable code, and can be invoked.
Numbers are primitives, and thus you cannot access/assign properties to/from them.
In Javascript, a function is an object, so you can set properties on it:
function a() {};
a.foo = 30;
a.foo; // evaluates to 30
A number literal however creates a primitive value (which has no properties) and not an object.
a = 20; // Create primitive value using number literal
When you set a property on a primitive value, in fact you create a wrapper object around the primitive value and set the property on the wrapper object, not the primitive value.
a.foo = 30; // wrapper object created and directly thrown away
(new Number(a)).foo = 30; // --> equivalent
When you read the property, again you create a wrapper object, whihch hasn't the property defined.
a.foo; // wrapper object created, has no foo property
(new Number(a)).foo; // --> equivalent, evaluates to undefined
In javascript functions are objects. Or they inherit from objects, or something.
try doing
a = function(){};
alert(typeof a.prototype);