I have a small question in JavaScript.
Here is a declaration:
function answerToLifeUniverseAndEverything() {
return 42;
}
var myLife = answerToLifeUniverseAndEverything();
If I do console.log(myLife), it will print 42, as I am just invoking the same instance of function resulting in 42 as the answer. (Basic rule on JavaScript that only references of objects are passed and not the object.)
Now, on the other, hand if I do:
var myLife = new answerToLifeUniverseAndEverything();
then I can't invoke the function; instead, myLife becomes just an object? I understand that this is a new copy of the same function object and not a reference, but why can't I invoke the method?
Can you please clarify the basic fundamental I am missing here?
By prefixing the call to answerToLifeUniverseAndEverything() with new you are telling JavaScript to invoke the function as a constructor function, similar (internally) to this:
var newInstance = {};
var obj = answerToLifeUniverseAndEverything.call(newInstance); // returs 42
if (typeof obj === 'object') {
return obj
} else {
return newInstance;
}
JavaScript proceeds to initialize the this variable inside the constructor function to point to a new instance of answerToLifeUniverseAndEverything. Unless you return a different Object yourself, this new instance will get returned, whether you like it or not.
When you do var myLife = answerToLifeUniverseAndEverything();, myLife is simply holding the return value from the function call - in this case, 42. myLife knows nothing about your function in that case, because the function was already called, returned, and then it assigned the resulting value (42) to the new variable myLife.
A completely different thing happens when you do var myLife = new answerToLifeUniverseAndEverything(); - instead, a new object is created, passed to the function as this, and then (assuming the function doesn't return an object itself), stored in the newly created variable. Since your function returns a number, not an object, the newly generated object is stored.
Try:
function answerToLifeUniverseAndEverything() {
return 42;
}
var myLife = answerToLifeUniverseAndEverything;
alert(myLife());
When you do:
var myLife = answerToLifeUniverseAndEverything();
you're assigning the function result to myLife ie 42.
I think i've described the behaviour of new elsewhere. Basically when you do new f() the JS engine creates an object and passes that as this, then uses that object if the return value of f() is not an object.
eg.
o = new f();
is equivalent (approximately) to
temp = {};
temp2 = f.call(temp);
o = typeof temp2 === "object" ? temp2 : temp;
If I do console.log(myLife) It'll print 42, as I am just invoking the same instance of function resulting in 42 as the answer. (Basic rule on javascripts that only references of objects are passed and not the object)
Not quite. This is because you're assigning the return value of answerToLifeUniverseAndEverything() to myLife. If you wanted to make a copy of the function, drop the brackets:
var myLife = answerToLifeUniverseAndEverything;
console.log(myLife());
Related
I am currently learning javascript and came across this example
var t = function()
{
this.name = "Jam";
no = "123";
}
console.log(t.no); //Undefined
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
t is a function object. As any other object, the function may have properties assigned. So in order your code to work you shall assign "123" to no property of your function (line A):
var t = function()
{
this.name = "Jam";
}
t.no = "123"; // line A
console.log(t.no); // "123"
var m = new t();
console.log(m.name);
Why is the first statement undefined ?
Because t doesn't have a property no.
First of all, the code inside the function, namely
this.name = "Jam";
no = "123";
is only executed when the function is called. You are doing this with var m = new t();, which comes after console.log(t.no);.
Secondly, no = "123"; does not create a property on the function object. It will attempt to set the value of variable no. Since the variable doesn't exist in your example, that line will either create a global variable no, or throw in error if your code is in strict mode.
Consider the following example:
var no = 21;
function foo() {
no = 42;
}
console.log(no); // 21
foo();
console.log(no); // 42
Because t is a function, that would be executed by t();. no on the other hand is a global scooed variable that is reached without prefix from everywhere.
t is a function expression. You you can access a returned object of a function like t().no or you can create a new object by using the function as a constructor like this
myT = new t()
console.log(t.no);
But your no variable is just a global variable inside the function and it is not a part of what it returns nor it is not attached to the returning object of the constructor function.
Here is a very good tutorial which covers all these topics at depths.
Why does 'this' need a local reference? The first code segment works fine but the second code segment thows an error... you're creating a reference (not being passed by value) so you would be modifying the same object prototype which is what you're trying to do anyways... not sure where the error is coming from...
Difference in code is the first few lines of each segment
First Code Segment:
Function.prototype.construct = function(aArgs) {
var a = this;
var fNewConstr = function() { a.apply(this, aArgs); };
fNewConstr.prototype = a.prototype;
return new fNewConstr();
};
function MyConstructor() {
for (var nProp = 0; nProp < arguments.length; nProp++) {
this['property' + nProp] = arguments[nProp];
}
}
var myArray = [4, 'Hello world!', false];
var myInstance = MyConstructor.construct(myArray);
Second Code Segment:
Function.prototype.construct = function(aArgs) {
var fNewConstr = function() { this.apply(this, aArgs); };
fNewConstr.prototype = this.prototype;
return new fNewConstr();
};
function MyConstructor() {
for (var nProp = 0; nProp < arguments.length; nProp++) {
this['property' + nProp] = arguments[nProp];
}
}
var myArray = [4, 'Hello world!', false];
var myInstance = MyConstructor.construct(myArray);
In the first bit of code, your "construct" method, the value of this will be the function from which you invoke it. It's not the newly-constructed object that the function intends to return from its internal constructor.
The value of this is set for each function call, depending on the nature of the call. You're calling "fNewConstr" via new, so the value of this in that function is the object being constructed. In order to correctly apply the original "root" function to the newly-created object, it requires a reference to that function. That's what's saved in "a".
Step by step, when you call
MyConstructor.construct(myArray);
what happens is:
The symbol "MyConstructor" is examined and found to be a reference to an object — a function, in fact.
The . operator results in the property "construct" being looked up, first on the "MyConstructor" function object itself and then on the object's prototype, where the property name will be found.
The value of that "construct" property is a function, so the ( ) operator launches a function call.
Now we're inside "construct", and this is a reference to the function "MyConstructor". That reference is immediately copied to "a".
The "fNewConstr" variable is initialized and then immediately invoked via new. A new object is created and now that function call is launched, with this referring to that new object.
Inside "fNewConstr", "a" still refers to "MyConstructor", so that reference lets the runtime find the "apply" function on the Function prototype. Now that function is launched, and we'll finally end up in the "MyConstructor" code.
Inside "MyConstructor", this will refer to the object constructed with the new operation, and the parameters will be the elements of the array passed in originally.
I have the following piece of code:
var Test = function () {
};
Test.prototype.doSomething = function() {
return "done";
};
Now, I create an object of Test
var t = new Test();
alert(t.doSomething()); // Correct alerts "done"
Now I add another method to the prototype:
Test.prototype.fly = function() { return "fly"; };
alert(t.fly()); // Correctly alerts "fly" (existing objects get "live udpated")
Now, I make the prototype point to a blank object:
Test.prototype = {};
alert(t.doSomething()); // Continues to alert "done", but why?
alert(t.fly()); // Continues to alert "fly", but why?
var t2 = new Test();
alert(t.doSomething()); // As expected, this does not work
When I add a method to prototype, it reflects correctly on all new and existing objects
When I "blank" out the prototype by doing <name>.prototype = {};, it only "blanks" out new instances, but not existing ones. Why?
An analogy is this:
var a = {foo : 'bar'};
var b = a; //the same object as a
var c = a;
var d = a;
a.apple = 'orange';
a = 1; //a === 1. b, c and d stay the same, pointing to the object with apple
What I did here is replace what a was pointing, but not the object itself.
When you added fly, you are modifying that single prototype object which all instances share, the object that Test.prototype is currently pointing to.
But when you assigned a blank object to Test.prototype, you modified what Test.prototype was pointing to. It does not modify what the existing instances are pointing to. But from this point on, any new instances will now use the new object on Test.prototype as their prototype object.
If you are familiar with C, I'd rather think of JS variables as pointers rather than references.
I'm completing the previous answer, if you want reflect your changes on all instances you must update the prototype correctly.
Prototype property is an object and you can delete a propery with the reserved keyword 'delete'.
If you want delete 'doSomething' property :
delete Test.prototype.doSomething;
consider
function Foo(){}
Foo.prototype = {a:{"VMAddress":"#1234"}}
consider that Foo.prototype.a object has VMAddress "#1234"
if you create object now then,
var f1 = new Foo();
now f1.a will point to the same object ie with Virtual Machine address "#1234"
if you look
f1.a === Foo.prototype.a ;//prints tue
If you change prototype to some other value now,
Foo.prototype = {a:{"VMAddress":"#5678"}}
and if you create object now then,
var f2 = new Foo();
although
f2.a === Foo.prototype.a; //prints true because both point to same VM address #5678
but
f1.a === f2.a; //prints false
why??
because their VM address are different(one is #1234 and other is #5678) and infact they are different object
final verdict
the prototype chain at the time of object creation decides what an object's prototype will be.
I have an object defined outside the function, in a global scope. This object is not passed into the function as an argument, but the function does modify it and return the modified object.
What I wanted to know is, if the function returns a copy of the object, or the original global object?
Also, will passing that object to the function as an argument, make a difference, since objects are passed into functions by reference?
Whenever you're returning an object, you're returning a reference to the object. Likewise, when you're passing an object, you're passing a reference. However, passing an object in as an argument can be different than just changing an object in global scope, as these examples show. This is because the reference to the object is itself passed by value.
If you're changing the members of an object, then whether you pass it in as an argument or just update the global object makes no difference. Either way, you're working with the same object.
Example 1:
var object = {foo:'original'};
function changeObject() {
object.foo = 'changed';
return object;
}
console.log(changeObject()); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'changed'}
Example 2:
var object = {foo:'original'};
function changeArgument(object) {
object.foo = 'changed';
return object;
}
console.log(changeArgument(object)); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'changed'}
On the other hand, if you're overwriting the object with a new object, the change won't persist if you do it to the argument, but will persist if you do it to the global object. That's because the argument passes the reference to the object by value. Once you replace this value with a reference to a new object, you're not talking about the same object anymore.
Example 3:
var object = {foo:'original'};
function replaceObject() {
object = {foo:'changed'};
return object;
}
console.log(replaceObject()); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'changed'}
Example 4:
var object = {foo:'original'};
function replaceArgument(object) {
object = {foo:'changed'};
return object;
}
console.log(replaceArgument(object)); // outputs {foo:'changed'}
console.log(object); // outputs {foo:'original'}
May be late comment, but this is typical challenge in any language.
Objects created on the heap and passed around by reference opposed to primitives(by value).
I think the root of the question is shared instance versus unique one to avoid unwelcome effects.
For example we calling a function to get a template(object) for new user to add to collection or want to clear the form
on cancel event from different modules to start over. It easy to understand and easy to overlook..test cases typically
not covering all usage permutations
The sanity checklist:
Here the shared instance:
var bigo = {
usr: { name: 'steven' },
bigi: function () {
return this.usr;
}
};
var outA = bigo.bigi();
var outB = bigo.bigi();
print(outA.name); // => steven
print(outB.name); // => steven
outA.name = 'ilan'; // change value
print(outA.name); // => ilan
print(outB.name); // => ilan
Non shared instance:
var bigo = {
bigi: function () {
var user = { name: 'steven' };
return user;
}
};
var outA = bigo.bigi();
var outB = bigo.bigi();
print(outA.name); // => steven
print(outB.name); // => steven
outA.name = 'ilan'; // change value
print(outA.name); // => ilan
print(outB.name); // => steven
What I wanted to know is, if the function returns a copy of the object, or the original global object?
Effectively, you only ever deal with references to objects in JavaScript. Even var foo = {} just assigns a reference to a new object to foo.
If the object is outside the function, you don't need to 'return' it. If you modify the object within the function it will update the object itself. Then you can reference the newly updated object in other functions as needed.
From your question this is how I think your code looks (more or less):
var o = {};
function f() {
o.prop = true;
return o;
}
In this case the global variable o references an object.
When you modify o you're modify whatever o references. Hence it modifies the original object.
When you return o you're returning a reference to the original object.
Passing the object to a function results in the reference to the original object being passed. Hence any modifications will affect the original object. For example:
var o = {};
f(o);
console.log(o.prop); // true
function f(o) {
o.prop = true;
}
When trying executing the two following code blocks separately:
The first one:
function Hallo() {
}
var some_obj = {
name: "Fred",
age: 23,
}
Hallo.prototype = some_obj;
var obj = new Hallo();
obj.constructor;
And the second one:
function Hallo() {
}
Hallo.prototype.name = 'Khanh';
Hallo.prototype.age = 23;
var obj = new Hallo();
obj.constructor;
I got the result in firebug's console is "Object{}" for the first and "Hallo()" for the second.
While the second one is pretty simple to understand but the first one is really odd. Because as I know the constructor of the obj Object in the first one is still the same (that is Hallo() function).
However I got Object() function in result.
I really cann't understand why. Could you help me with it?
Thank you!
The reason is:
When you do var obj = new Hallo();, then
console.log(obj.constructor === Hallo.prototype.constructor); // true
In your first example, you assigned Hallo.prototype with a new object, whose constructor is function Object (function Object(){...}).
In your second example, the Hallo.prototype.constructor is still function Hallo() {...}
prototype will get a reference that point to the constructor by default, int the first function you overwrite the prototype to some_obj, the constructor reference changes at the same time to some_obj's constructor reference --Object's constructor Object()