Hello I am a javascript beginner and I read about how to interpret the this keyword but in this example I am still confused:
<script>
function example(param) {
this.a = param;
var b = true;
this.getB = function() {
return b;
}
this.setB = function(x) {
b = x;
}
}
document.write(window.a); //prints "undefined" (line A)
document.write(window.b); //prints "undefined" (line B)
document.write(window.getB(); //generates an error "undefined is not a function (Line C)"
For Line A, I guess the explanation is that this refers to the object that owns the function example, in this case, the window object. Therefore you can reference window.a, but it is not defined so it prints undefined
I cannot understand Line B. Isn't var b restricting b to a local scope? Which should mean that you can only reference b within the function? I was expecting Line B to generate an error, not printing undefined.
I have totally no clue about Line C, why does it generate an error, and what does this mean in this case?
Many thanks!
The this variable depends on how the function is being used.
If you instantiate the function (treat it like a class) then this will refer to the instance of the class:
new example("foo").getB(); // `this` is an instance of example
If you call it with Function.prototype.call then this will refer to whatever object you passed to the call method:
example.call(anotherObject, "foo"); // `this` refers to anotherObject
If you just execute it directly then this may refer to the window or whatever other surrounding scope.
example("foo"); // `this` likely refers to the window object
You can also use Function.prototype.bind to wrap your function in a specific scope to help clear things up:
var wrappedExample = example.bind(aSpecificObject);
wrappedExample(); // Doesn't matter how it's called, `this` will refer to aSpecificObject
Of course you may want to consider your target browsers before using bind (or use a polyfill).
First - you never call the example() function. So nothing it does has any effect.
Second - you have a syntax error on the last line, you are missing a ).
If you were to fix those issues, the results would be different:
function example(param) {
this.a = param;
var b = true;
this.getB = function() {
return b;
}
this.setB = function(x) {
b = x;
}
}
example("data");
document.write(window.a); //prints "undefined" (line A)
document.write(window.b); //prints "undefined" (line B)
document.write(window.getB()); //generates an error "undefined is not a function (Line C)"
For Line A, I guess the explanation is that "this" refers to the object that owns the function "example", in this case, the window object. Therefore you can reference window.a, but it is not defined so it prints "undefined"
It wasn't defined because you weren't calling the function. If you were to call it, then it is defined as the value of param.
I cannot understand Line B. Isn't "var b" restricting b to a local scope? Which should mean that you can only reference b within the function? I was expecting Line B to generate an error, not printing "undefined".
The locally scoped variable b is distinct from the b property of the object that is stored in this.
Accessing an undefined property of an object doesn't throw an error in JavaScript, it gives you undefined.
If you were to access an undeclared variable, then you would have a Reference Error.
I have totally no clue about Line C, why does it generate an error, and what does "this" mean in this case?
As with the other examples, you don't use this for lines A, B or C. You use window. this is only used inside the function.
You were getting an error because you never ran the function, so window.getB was never set. Calling the undefined value as a function throws an error.
If you fix the problem, as described above, you get the value of the local b variable.
If you are using this within a function, it will refer to the global window object.
function whatsThis(){
console.log(this);
}
whatsThis(); //displays the window object
If the function exists within an object created by a constructor function, however, this will refer to the parent object
var WhatsThis = function(){
this.showThis = function(){
console.log(this);
};
};
var obj = new WhatsThis();
obj.showThis(); //displays the instance of WhatsThis
Likewise with object literals, this refers to the containing object.
Related
Functions are callable objects in javascript so is it possible to redefine a function definition
?
so basically what I am trying to do is:
let a = function(){console.log('first');}
let b = a;
/**
* alter definition of function 'a' something like:
a.redefine(function(){console.log('second');});
*/
b();//should log 'second'
I looked up for javascript function documentation here as well as here but couldn't find any reference on how/where functions definition are actually stored, even tried to inspect a.prototype as well as altering a.prototype.constructor but got nothing.
A workaround on this can be something like:
let functionHolder={
a:function(){console.log('first');}
}
let a = function(){functionHolder.a();}
let b = a;
functionHolder.a=function(){console.log('second');}
b();//would log 'second'
however my ultimate goal is to understand how functions actually work/stored in js and not a workaround just to achieve this.
It's not possible. Once a function has been defined, its "function code" is immutable. The only way for a variable name which references a function to reference different "function code" would be to reassign the variable name to a new function.
Your
functionHolder.a=function(){console.log('second');}
is essentially the same thing - you're reassigning the function that functionHolder.a refers to.
In the specification, when you call a function, you invoke its internal method [[Call]], which eventually leads you to
OrdinaryCallEvaluateBody, which does:
Return the result of EvaluateBody of the parsed code that is F.[[ECMAScriptCode]] passing F and argumentsList as the arguments.
And the ECMAScriptCode internal property is not reassignable - it's only set in FunctionInitialize, when a new function is created (like when you do
<someIdentifier> = function(){console.log('second');}
)
It is possible by turning the b variable into a getter on the window object.
It's not pretty, but it works:
let a = () => "foo";
console.log("a", a());
Object.defineProperty(window, 'b', {
get() { return a; }
});
console.log("b", b());
a = () => "bar";
console.log("b", b());
a is a reference to a function. Call it a "pointer".
b is a copy of that pointer.
When you re-assign a, you're replacing the pointer. If another variable still points to the value behind the old pointer, that value still exists. That's why in your example, b didn't change: b was a pointer to the old function.
That getter is basically a function that's executed without explicitly having to call it, so it always gets the latest result of whatever the getter function does.
I'm currently studying javascript by following the book "you dont know js" series.
In the "this & object prototype" section, when discussing "indirect references to functions", the author states
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // 2
The result value of the assignment expression p.foo = o.foo is a
reference to just the underlying function object. As such, the
effective call-site is just foo(), not p.foo() or o.foo() as you might
expect. Per the rules above, the default binding rule applies.
So apparently, (p.foo = o.foo) return a reference to the function foo. But what is the mechanism/rules that allow (p.foo = o.foo) return a reference to the function foo? In other words, why a simple assignment return a reference to foo function?
When I want to understand something like this, I find it helpful to break it down step by step.
o.foo looks at the o object and finds a property named foo. It returns a reference that property, whatever it might be. In this case, the o.foo property is a reference to the function foo.
p.foo = o.foo takes the result from above (a reference to the function foo), creates a property in the p object which is also named foo. So now p.foo is also a reference to the foo function, exactly the same thing as o.foo.
That expression is wrapped in parentheses, so now you have whatever was on the left side of the = sign, or p.foo, which is (as a reminder) still a reference to the foo function.
Now we find the () at the end. This calls whatever function we have on hand at this moment. That is the foo function. Note in particular that we are not calling p.foo(). That would be a method call to the function that p.foo is a reference to, so inside that function, this would be set to p. But we're not doing that. We're just calling whatever function was returned by ( p.foo = o.foo ). As before, this is the same foo function, but we've now lost any connection it may have ever had to the o object or the p object.
So, when we make that call at the end, we are merely calling the foo function without setting this to any particular object. Because of that, when we make the call, this is set to undefined.
But we're not running in strict mode, so JavaScript "helpfully" doesn't want to give us an undefined this, so it sets this to the window object in a browser or the global object in Node.
Previously we did var a = 2;. So the window or global object actually now has a property named a, and the value of that property is 2.
So now when we do the console.log(this.a), we pick up the a property from the window or global object. That value is 2.
What if all this code was not running at the global level, but instead it was inside a function? What would happen then?
function test() {
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // was 2, but now is undefined
}
test();
Now when we call console.log( this.a ); inside foo, this still refers to the window or global object. But when we set var a = 2;, we aren't setting a global property any more. We're just creating a local variable. window.a or global.a is undefined (unless some other code previously set it).
Strict mode avoids some this weirdness. If we put a 'use strict'; at the top of the code, it will compile in strict mode. And now when we make that last function call at the end, where we're calling the foo function (again not as a method!), this is now set to undefined instead of window or global. Therefore, when we try to call console.log(this.a), the code fails because this is the same as undefined, and undefined does not (and couldn't) have an a property.
Let's try it:
'use strict';
function foo() {
console.log( this.a );
}
var a = 2;
var o = { a: 3, foo: foo };
var p = { a: 4 };
o.foo(); // 3
(p.foo = o.foo)(); // was 2 in the original, but now throws an exception
The bottom line, at least for this particular example: always use strict mode! It is your friend.
The assignment operator = is an expression in JavaScript that produces (returns) the assigned value. Because it is an expression it can be used anywhere an expression is allowed, such as inside parenthesis.
For example:
let test = (a = b = c = { name: 'test' })
The code above would first evaluate the expression in the parenthesis and point the variables c, b, and a to the test object (in that order), then it would point test to the produced value from this expression. After that line is executed, a, b, c, and test will all point to the same object.
Similarly,
(p.foo = o.foo)
Would produce o.foo back (technically it would produce whatever o.foo is pointing to, which is the function foo).
As far as
(p.foo = o.foo)()
By adding the additional () after the parenths, we are telling the engine that we want to invoke whatever the expression (p.foo = o.foo) ends up producing. Thus we end up invoking the function foo. Similar patterns is used in IIFEs.
A helpful rewrite would be to think of the line above as doing this:
let produced = (p.foo = o.foo)
produced()
Further reading about statements vs expressions.
There is a test to whether a will be undefined or b will be undefined. However what is confusing me is, how are we accessing both a and b inside the console.log, when both are local variables. Does it have to do with the way we defined a function inside a parentheses ? I am new to JavaScript and I am trying to understand how it works.
(function(){
var a = b = 3;
})();
console.log("a defined? " + (typeof a !== 'undefined'));
console.log("b defined? " + (typeof b !== 'undefined'));
From outside the function, your console.log() calls can access b but not a. The variable a is declared in the function by the var statement; the variable b however is made implicitly global by the initialization expression for a.
The var statement is interpreted as if it were written
var a = (b = 3);
The syntax of a var statement is such that the initialization does not define b as a local variable. You can verify that with code like yours, or by adding
"use strict";
right before the var statement and noting that you'll get an error from the implicit global use of b.
Well, in your example, you have a "global" variable b and a local variable a. Only b is visible outside of the function.
When you try to refer to a in your console output, it will refer to a "global" variable a (which is not yet defined).
b is "global" because there is no var statement that defines it. The var on that line only applies to a.
a is a local variable only visible inside of the function (nicely declared with var). However, you can still have variables in other parts of your program that are also called a. Your console output is an example for this. But these are completely separate variables.
Variable a is inside a IIFE, so it won't be visible in a parent scope (it has var keyword behind it).
Variable b is presumed to be from outer scope of the IIFE (it does not have var keyword).
The way typeof operator works, it lets you put any valid variable name for a type test, even undeclared i.e.:
var a = 5;
var b;
console.log(typeof a); // number
console.log(typeof b); // undefined
console.log(typeof totallyNotMentionedAnywhereElse); // undefined
I thought this was suppose to trigger a bug when calling a function from within a function using the keyword this. Running Chrome 37.0
S = function () {
this.x = "test";
this.test1 = function () {
console.log("test1");
this.test2();
};
this.test2 = function () {
console.log(this); // output is s object, thought 'this' suppose to be global?
console.log("test2");
};
};
s = new S();
s.test1();
Edit:
I mixed it up with this code:
s = function () {
console.log(this);
var t = function () {
console.log(this);
};
t();
};
x = new s();
Calling a function as part of an expression where you get the function reference from an object property calls the function with this set to the object you got the property from.
So your code:
s.test1();
...will call test1 setting this equal to the object referenced by s, because the call is part of an expression getting the test1 function reference from a property on the object s refers to.
Then in test1, when you do:
this.test2();
...it calls test2 setting this equal to the object referenced by this, because the call is part of an expression getting the test2 function reference from a property on the object this refers to.
Where you'd run into a problem with this not being set correctly would be if you didn't call test1 or test2 via an object property, like this:
var f = s.test1;
f();
Then, within the call to test1, this would be the global object (in loose mode) or undefined (in strict mode). The same sort of thing happens when passing a function as an argument:
foo(s.test1);
If foo calls the function its first argument relates to, we get the same thing (this = global object or undefined).
Since you're not doing that, though, that doesn't happen.
The key thing here is how the function is called, not where it's defined (inside another function or not).
More on this in How Does The this Keyword Work? here on SO, and in Mythical methods and You must remember this on my blog.
I know about function scope, self-executing functions, hoisting and other cool words, but I am totally of-base with the following.
var o = {
a : 1,
f : (function(){
console.log(this.a);
})()
};
Why is this gives me undefined? Function is self-executed right when the object is initialized, therefore I expect it to already assign a to 1. But it is not doing this.
The function is not called in the scope of your newly created object, but in the global scope. There, a (or window.a) is indeed undefined. The braces themselves do not create a new block. Only when you call a method later, this is bound to o as you assume. Compare it with this:
var o = {
a : 1,
f : function(){
console.log(this.a);
}
};
o.f(); // console prints 1
Because you're not calling it via o, its this is not o.
Additionally you never assing the function to anything either - f is set to be the return value (undefined).