I am defining the consctructor function Foo as:
function Foo () {
var a= 0;
this.b = 1;
this.f1= function () { return a; };
this.f2= function () { return b; };
}
and I am creating the object as:
var bar= new Foo();
return b does not work, I have to use return this.b instead. But it work fine with the a variable. Why?
When your function
f1
is invoked, it asks it's environment/context "Excuse me, do you have any idea what [a] is please?". It responds with "Not here, but let me ask my own environment/context whether or not it knows what [a] is... ah hah, it says it knows what [a] is, and it's value is 0.".
When your function
f2
is invoked, it asks it asks the same question of [b] as f1 did for [z]... the difference is, the environment/context it is looking in for [b] does not include the instance of Foo to which [b] has been attached.
'this' in JavaScript is a tricky subject, and is covered in great detail in this free online book which is part of the series "You don't know JS", by Kyle Simpson. A very good series.
It doesn't return because b is not declared.
function Foo () {
var a = 0;
this.b = 1;
var b = this.b;
this.f1= function () { return a; };
this.f2= function () { return b; };
}
should work fine.
Or you can bind f2 method to "this" and return it:
function Foo () {
var a= 0;
this.b = 1;
this.f1= function () { return a; };
this.f2= (function () { return this.b; }).bind(this);
}
Your function declares two types of constructs. var a is a normal variable that is scoped to the function and it is private within the function. It can be used any way the function needs to, so returning it is no problem at all. Most importantly, its value will not change from one instance of Foo to the next.
b, f1 and f2 aren't being declared as variables. They are being declared as "instance properties", meaning that their data will change from one instance of the object to another. If you want to return an instance property value, you must use this to return the value associated with that particular instance.
After all, if you wrote:
var obj1 = new Foo();
obj1.b = 10;
var obj2 = new Foo();
obj1.b = 20;
How would you be able to keep the two b values separate from each other? The answer is that you have two instances of a Foo object and the obj1 and obj2 variables each store a reference to their own instance.
When you write obj1.b, you need access to the b property that belongs to the obj1 object instance. The this keyword does this for you.
Read on for more details:
The this object binding is volatile in JavaScript...that is, it doesn't always point to the same object and its binding can change from one line of code to the very next. How you invoke the code that contains the word this determines what object it will bind to.
Here's a checklist that you can follow to know what this will bind to...
If the code that contains this is invoked:
As a method or property of an object instance (through an instance variable):
var o = new Object();
// "this" will be bound to the "o" object instance
// while "someProperty" and "someMethod" code executes
o.someProperty = someValue;
o.someMethod();
Via a .call(), .apply(), .bind() or Array.prototype.fn invocation:
// "this" will be bound to the object suppled as the "thisObjectBinding"
someFunction.call(thisObjectBinding, arg, arg);
someFunction.apply(thisObjectBinding, [arg, arg]);
var newFunc = someFunction.bind(thisObjectBinding, arg, arg);
Additionally, several Array.prototype methods allow for a thisObject to be passed which will alter the binding for the duration of the method call:
Array.prototype.every( callbackfn [ , thisArg ] )
Array.prototype.some( callbackfn [ , thisArg ] )
Array.prototype.forEach( callbackfn [ , thisArg ] )
Array.prototype.map( callbackfn [ , thisArg ] )
Array.prototype.filter( callbackfn [ , thisArg ] )
If none of the other scenarios apply, Default binding occurs.
3a. With "use strict" in effect: this is undefined
3b. Without "use strict" in effect: this binds to the Global object
** NOTE: this binding can also be affected by using eval(), but as a general best practice, the use of eval() should be avoided.
Related
I'm new to JavaScript so this is possibly a trivial question:
I'm trying to construct an object that stores a mapping from a set of integers to some of its methods, i.e. something like this:
'use strict';
function Foo() {
this.funcs = {
1: this.func1,
2: this.func2,
}
}
Foo.prototype.func1 = function() {
this.prop = 1;
}
Foo.prototype.func2 = function() {
this.prop = 2;
}
I'd then like to be able to call methods of Foo like this:
foo = new Foo();
var func = foo.funcs[1];
func();
But this results in: Cannot set property 'prop' of undefined, i.e. this does not refer to foo.
What's the problem here and is there a better way to implement this?
Your problem is this line:
var func = foo.funcs[1];
JavaScript determines the value of this based on how a function is called. If you use dot notation, such as foo.funcs[1](); then the value of this will associated with the foo object. But when you run func(), that's just a plain function and this will have the default value of undefined.
It would be worth your time to read the two chapters of You Don't Know JS that discuss this. It should take less than an hour to learn, and you'll be way ahead of most JS programmers once you learn it.
The rules might not make sense until you read the chapter, but they are summarized below:
Determining the this binding for an executing function requires
finding the direct call-site of that function. Once examined, four
rules can be applied to the call-site, in this order of precedence:
Called with new? Use the newly constructed object.
Called with call or apply (or bind)? Use the specified object.
Called with a context object owning the call? Use that context object.
Default: undefined in strict mode, global object otherwise.
Based on the above rules, the code below is the simplest way you could get it to work the way you are expecting it to:
'use strict';
function Foo() {
this.funcs = {
1: this.func1,
2: this.func2,
}
}
Foo.prototype.func1 = function() {
this.prop = 1;
console.log('called func1. this.prop =', this.prop);
}
Foo.prototype.func2 = function() {
this.prop = 2;
console.log('called func2. this.prop =', this.prop);
}
const foo = new Foo();
foo.funcs[1]();
There are a few ways to achieve what you require, however the most robust approach is to bind() each function to the instance of Foo() that is being instantiated.
This can be done by passing this to bind() of each function:
this.func1.bind(this)
Using bind() in this way ensures that this, for func1 and func2 is defined as the instance of Foo(). This in turn ensures that this.prop can be accessed and assigned as expected:
'use strict';
function Foo() {
this.funcs = {
/* Bind the functions to this Foo() instance */
1: this.func1.bind(this),
2: this.func2.bind(this),
}
}
Foo.prototype.func1 = function() {
this.prop = 1;
console.log('called func1. this.prop =', this.prop);
}
Foo.prototype.func2 = function() {
this.prop = 2;
console.log('called func2. this.prop =', this.prop);
}
const foo = new Foo();
var func = foo.funcs[1];
func();
foo.funcs[2]();
Another key thing to note is the bind() based approach above ensures that, if you acquire and call a reference to one of the functions on the funcs field as shown in your original post, that it will work as expected:
/* As per original post - doing this is not possible without .bind() */
var func = foo.funcs[1];
func();
Without the use of bind(), this method of acquiring and calling func will fail due to func not being bound to the instance of Foo.
In the following code, the first function is not bound to obj, but the second function is, so f() returns fifi and g() returns Mark Twain as expected. But the 3rd attempt, it is by (obj.getCallBack) first, which is now a function, and then it is invoked, essentially it should be the same as the f case. But they do print out Mark Twain instead. Why are they not bound to obj using bind() but still got executed with this pointing to obj?
(the 4th attempt is just a usual invocation of a method, and this should bind to the object on which the method is invoked on).
(tested on the current Chrome, Firefox, and IE 9)
window.name = "fifi";
var obj = {
name: "Mark Twain",
getCallBack: function() {
return this.name;
}
}
var f = obj.getCallBack;
var g = f.bind(obj);
console.log(f);
console.log(f());
console.log(g);
console.log(g());
console.log((obj.getCallBack)());
console.log(obj.getCallBack());
You are forgetting that if a function is called as a property of some object, that object will be the this for the call. So:
obj.getCallBack() //The function referenced by `obj.getCallBack`
//is called as a property of `obj`, so obj will be `this`
//for the call
f() //The function referenced by f, is not called as a property of some object so
//`this` will depend on strict mode.
After these basic rules, the bound function will be invoked, which can be thought of as a proxy function (any shim does this) that uses .call/.apply to explicitly set the context for the target function. So the this value for the proxy function doesn't matter, but behind the scenes it was set by the basic rules.
Edit:
(obj.getCallBack) does not return the function as value, because getValue is not called.. So it is exactly the same as obj.getCallback and the first post applies.
So you can do this and not get an error:
(obj.getCallback) = 5;
As opposed to:
(function(){}) = 5; //invalid assignment
To complement Esailija's answer, the desired effect actually should be:
var obj = {
name: "Mark Twain",
getCallBack: function() {
return function() { return this.name; };
}
}
var f = obj.getCallBack();
var g = f.bind(obj);
console.log(f);
console.log(f());
console.log(g);
console.log(g());
console.log((obj.getCallBack())());
console.log(obj.getCallBack()());
console.log(obj.getCallBack().bind(obj)());
Then in this case, the third attempt will give fifi, and so will the 4th attempt. To get the name inside obj, the fifth attempt binds it and invoke it and will get Mark Twain.
But the method that returns the callBack function should bind it, so let's change the code to:
var obj = {
name: "Mark Twain",
getCallBack: function() {
return (function() { return this.name;}).bind(this); // <-- note here
}
}
and now all attempts, even f(), will return Mark Twain.
what is the this (inside inner functions) referring to in the following code context? Does it point to TimeSpan?
var TimeSpan = function (days, hours, minutes, seconds, milliseconds) {
var attrs = "days hours minutes seconds milliseconds".split(/\s+/);
var gFn = function (attr) {
return function () {
return this[attr];
};
};
var sFn = function (attr) {
return function (val) {
this[attr] = val;
return this;
};
};
}
Thanks
The this value is set implicitly depending on how the function is invoked, there are three cases where this happens:
When a reference with no base-object, or a non-reference is invoked:
myFn(); // the myFn reference has no base object
(function () {})(); // non-reference
The this value will point to the global object 1
When a reference contains a base object, for example:
myObj.method();
The this value inside method will point to myObj.
When the new operator is used:
var obj = new Foo();
The this value inside the Foo function, will point to a newly created object that inherits from Foo.prototype.
The this value can be set also explicitly, by using the call and apply methods, for example, with call:
function test(a) {
return alert(this + a);
}
test.call("hello", " world"); // alerts "hello world"
Or with apply if we need to "apply" a set of arguments from an array to a function:
function test(a, b) {
return alert(this + a + b);
}
var args = ["my ", "world "];
test.apply("hello ", args); // alerts "hello my world"
[1] This has changed on the new ECMAScript 5th Strict Mode, now when a function reference with no base object, or a non-reference is invoked (as the first case), the this value will contain undefined.
This was made because when working with constructor functions, people often forgot to use the new operator when calling the constructor.
When that happened, the this value pointed to the global object, and that ended up adding unwanted global properties.
Now on strict mode, this will contain undefined, and if property lookup is made on it (this.foo = 'foo') we will have a nice TypeError exception, instead of having a global foo property.
this refers to the current object, in this case the function you are inside of. Since everything in JavaScript is an object you can modify the attributes of a function object using the this keyword:
var f = function() {
this.key = "someValue";
}
console.log(f.key); // prints "someValue"
So in this case this should point to the function at the deepest scope level, and not TimeSpan.
What is the difference between declaring a variable with this or var ?
var foo = 'bar'
or
this.foo = 'bar'
When do you use this and when var?
edit: is there a simple question i can ask my self when deciding if i want to use var or this
If it is global code (the code is not part of any function), then you are creating a property on the global object with the two snippets, since this in global code points to the global object.
The difference in this case is that when the var statement is used, that property cannot be deleted, for example:
var foo = 'bar';
delete foo; // false
typeof foo; // "string"
this.bar = 'baz';
delete bar; // true
typeof bar; "undefined"
(Note: The above snippet will behave differently in the Firebug console, since it runs code with eval, and the code executed in the Eval Code execution context permits the deletion of identifiers created with var, try it here)
If the code is part of a function you should know that the this keyword has nothing to do with the function scope, is a reserved word that is set implicitly, depending how a function is called, for example:
1 - When a function is called as a method (the function is invoked as member of an object):
obj.method(); // 'this' inside method will refer to obj
2 - A normal function call:
myFunction(); // 'this' inside the function will refer to the Global object
// or
(function () {})();
3 - When the new operator is used:
var obj = new Constructor(); // 'this' will refer to a newly created object.
And you can even set the this value explicitly, using the call and apply methods, for example:
function test () {
alert(this);
}
test.call("hello!"); //alerts hello!
You should know also that JavaScript has function scope only, and variables declared with the var statement will be reachable only within the same function or any inner functions defined below.
Edit: Looking the code you posted to the #David's answer, let me comment:
var test1 = 'test'; // two globals, with the difference I talk
this.test2 = 'test'; // about in the beginning of this answer
//...
function test4(){
var test5 = 'test in function with var'; // <-- test5 is locally scoped!!!
this.test6 = 'test in function with this'; // global property, see below
}
test4(); // <--- test4 will be called with `this` pointing to the global object
// see #2 above, a call to an identifier that is not an property of an
// object causes it
alert(typeof test5); // "undefined" since it's a local variable of `test4`
alert(test6); // "test in function with this"
You can't access the test5 variable outside the function because is locally scoped, and it exists only withing the scope of that function.
Edit: In response to your comment
For declaring variables I encourage you to always use var, it's what is made for.
The concept of the this value, will get useful when you start working with constructor functions, objects and methods.
If you use var, the variable is scoped to the current function.
If you use this, then you are assigning a value to a property on whatever this is (which is either the object the method is being called on or (if the new keyword has been used) the object being created.
You use var when you want to define a simple local variable as you would in a typical function:-
function doAdd(a, b)
{
var c = a + b;
return c;
}
var result = doAdd(a, b);
alert(result);
However this has special meaning when call is used on a function.
function doAdd(a, b)
{
this.c = a + b;
}
var o = new Object();
doAdd.call(o, a, b);
alert(o.c);
You note the first parameter when using call on doAdd is the object created before. Inside that execution of doAdd this will refer to that object. Hence it creates a c property on the object.
Typically though a function is assigned to a property of an object like this:-
function doAdd(a, b)
{
this.c = a + b;
}
var o = new Object();
o.doAdd = doAdd;
Now the function can be execute using the . notation:-
o.doAdd(a, b);
alert(o.c);
Effectively o.doAdd(a, b) is o.doAdd.call(o, a, b)
var foo = 'bar'
This will scope the foo variable to the function wrapping it, or the global scope.
this.foo = 'bar'
This will scope the foo variable to the this object, it exactly like doing this:
window.foo = 'bar';
or
someObj.foo = 'bar';
The second part of your question seems to be what is the this object, and that is something that is determined by what context the function is running in. You can change what this is by using the apply method that all functions have. You can also make the default of the this variable an object other than the global object, by:
someObj.foo = function(){
// 'this' is 'someObj'
};
or
function someObj(x){
this.x=x;
}
someObj.prototype.getX = function(){
return this.x;
}
var myX = (new someObj(1)).getX(); // myX == 1
In a constructor, you can use var to simulate private members and this to simulate public members:
function Obj() {
this.pub = 'public';
var priv = 'private';
}
var o = new Obj();
o.pub; // 'public'
o.priv; // error
Example for this and var explained below:
function Car() {
this.speed = 0;
var speedUp = function() {
var speed = 10; // default
this.speed = this.speed + speed; // see how this and var are used
};
speedUp();
}
var foo = 'bar'; // 'var can be only used inside a function
and
this.foo = 'bar' // 'this' can be used globally inside an object
Consider this javascript code:
var bar = function () { alert("A"); }
var foo = bar;
bar = function () { alert("B"); };
foo();
When running this code I get "A". Is this behavior a part of javascript specification and can I rely on it?
In other examples, nothing was passed by value; everything was passed by reference.
bar and foo are BOTH pointers
All vars/handles to NON primitive objects in javascript are pointers; pointers ARE native to javascript, they are the default.
var bar = function () { alert("A"); } //bar is a pointer to function1
var foo = bar; //pointer copied; foo is now also a pointer to function1
bar = function () { alert("B"); }; //bar points to function2
foo(); //foo is still a pointer to function1
You will run into hidden errors and bugs if you think they are copies. Especially so if you work with complex objects. For example
function person(name){this.name = name}
var john = new person("john")
var backup = john
backup.name //john
john.name = "jack"
backup.name //jack, NOT john
To really COPY a non-primitive in javascript takes more work than just a = b.
For example:
function person(name){ this.name = name}
var john = new person("john")
var backup = new Object()
backup = JSON.parse(JSON.stringify(john))
backup.__proto__ = john.__proto__ //useful in some cases
john.name = "jack"
backup.name //john
Yes that is expected and by design.
Your question is basically: does foo reference bar as a pointer or reference would in another language?
The answer is no: the value of bar at the time of assignment is assigned to foo.
I'm a bit late here but I thought I'd give an answer anyways and flesh something out.
It's best not to think in terms of pointers and memory references when discussing the internals of JavaScript (or ECMAScript) when dealing with the specifications. Variables are environment records internally and are stored and referenced by name, not memory address. What your assignment statement is doing, internally and by design, is looking up the environment record name (either "foo" or "bar") and assigning the value to that record.
So,
var bar = function () { alert("A"); }
is assigning the environment record "bar" the value (anonymous function).
var foo = bar;
internally calls GetValue("bar") which retrieves the value associated with the record "bar" and then associates that value with the record "foo". Hence, afterwards the original value of bar can still be used as it's now associated with foo.
Because JavaScript references by string and not memory address is precisely why you can do things like this:
someObject["someProperty"]
which is looking up the value based on the property name.
You are assigning the value of an anonymous function to a variable not a pointer.
If you want to play with pointers, you can use objects that are passed by reference, not copy.
Here are some examples:
"obj2" is a reference of "obj1", you change "obj2", and "obj1" is changed. It will alert false:
var obj1 = {prop:true},
obj2 = obj1;
obj2.prop = false;
alert(obj1.prop);
"prop" points to a property that is not an object, "prop" is not a pointer to this object but a copy. If you change "prop", "obj1" is not changed. It will alert true:
var obj1 = {prop:true},
prop = obj1.prop;
prop = false;
alert(obj1.prop);
"obj2" is a reference to the "subObj" property of "obj1". if "obj2" is changed, "obj1" is changed. It will alert false:
var obj1 = {subObj:{prop:true}},
obj2 = obj1.subObj;
obj2.prop = false;
alert(obj1.subObj.prop);
Yes, there's nothing special about the fact that the variables are referring to functions, there's no aliasing involved.
var bar = 1;
var foo = bar;
bar = "something entirely different";
// foo is still 1
Yes, this is the correct behavior.
//create variable bar and assign a function to it
var bar = function () { alert("A"); }
//assign value of bar to the newly created variable foo
var foo = bar;
//assign a new function to the variable bar
//since foo and bar are not pointers, value of foo doesn't change
bar = function () { alert("B"); };
//call the function stored in foo
foo();
This is assigning a variable to an unnamed function, not a pointer to a function
Yes, you've created a pointer to the original "A" function. When you reassign bar, you're reassigning it, but you're still leaving any references to the old function alone.
So to answer your question, yes, you can rely on it.
Those are not function pointers (and there are no pointers in JS natively). Functions in JS can be anonymous and are first class objects. Hence
function () { alert("A"); }
creates an anonymous function that alerts "A" on execution;
var bar = function () { alert("A"); };
assign that function to bar;
var foo = bar;
assign foo to bar, which is the function "A".
bar = function () { alert("B"); };
rebind bar to an anonymous function "B". This won't affect foo or the other function "A".
foo();
Call the function stored in foo, which is the function "A".
Actually in languages where there are function points e.g. C it won't affect foo either. I don't know where you get the idea of getting "B" on reassignment.
void A(void) { printf("A\n"); }
void B(void) { printf("B\n"); }
typedef void(*fptr_t)(void);
fptr_t foo = A;
fptr_t bar = foo;
bar = B;
foo(); // should print "A"
I would just like to add this also works for pre-defined named functions as well:
function myfunc() { alert("A"); }
var bar = myfunc;
var foo = bar;
bar = function () { alert("B"); };
foo();
This will do the same thing, indicating that function names act like array names (pointers).
For each FunctionDeclaration f in code, in source text order do:
Let fn be the Identifier in FunctionDeclaration f.
Let fo be the result of instantiating FunctionDeclaration f as described in Clause 13.
Let funcAlreadyDeclared be the result of calling env’s HasBinding concrete method passing fn as the argument.
If funcAlreadyDeclared is false, call env’s CreateMutableBinding concrete method passing fn and configurableBindings as the arguments.
References
ECMAScript-5: Section 10.5