"this" is different when function called from local function object - javascript

I wrote the following code, where I would expect the calls to getFull() and useGetFull() to do the same thing, since useGetFull just gets a function object for getFull() and calls it.
function Person(name, family) {
this.name = name;
this.family = family;
}
Person.prototype.getFull = function() {
console.log(this.name + " " + this.family);
};
Person.prototype.useGetFull = function() {
const f = this.getFull;
f();
// f.call(this) works as expected
}
p = new Person("Bob", "Smith");
p.getFull();
p.useGetFull();
However, they don't do the same thing, because inside useGetFull(), "this" is the global object. I noticed that using f.call(this) instead of f() works as intended, but I can't wrap my head around why I have to use it. Why is the value of "this" different depending on how/where I call the function?

A simple rule:
a.b() //b called with context a
d.e() //e called with context d
c() // c called with no context ( so the global instead)
Javascripts context depends on how the function was called.

If you haven't noticed, this.getFull() also works. This is because when you invoke the function as a property of an object (any object), that function's this will refer to that object, the object you invoked the function on, in case of foo.bar(), foo, and in case of this.getFull(), this. This is why this example works as expected:
function Person(name, family) {
this.name = name;
this.family = family;
}
Person.prototype.getFull = function() {
console.log(this.name + " " + this.family);
};
Person.prototype.useGetFull = function() {
/* getFull is preceded by something, it is invoked through "this", so
the "this" reference inside of getFull will be set to the "this" part in
the statement "this.getFull()". */
this.getFull();
}
p = new Person("Bob", "Smith");
p.getFull(); prints out Bob Smith
p.useGetFull(); // prints out Bob Smith
However, when a function is invoked not as the property on an object, in other words, when it is not accessed in a way similar to either foo.bar() or foo["bar"](), but in a way like foo(), even if foo is a reference to a variable whose value is x.y, like f() in your example, its this will be bound to the global object, in a browser, that object is window.
function Person(name, family) {
this.name = name;
this.family = family;
}
Person.prototype.getFull = function() {
console.log(this.name + " " + this.family);
};
Person.prototype.useGetFull = function() {
const f = this.getFull;
/* this call is not preceded by any objects, it is a plain
traditional function call, it is a function invokation in its
simplest form. all functions invoked in this manner will have
their "this" reference set to the global object. */
f();
}
p = new Person("Bob", "Smith");
p.getFull();
p.useGetFull();
If you are interested in this (pun not intended), go here.

Related

How chaining of call and bind works internally?

I've a program with functions as Person, greet, specificCall and objects as kumar and chandra.
var fullName=""
function greet() {
console.log("Welcome "+ this.fullName);
}
var Person=function (name) {
this.fullName=name
console.log(this.fullName)
}
var chandra=new Person("chandrakumar")
var kumar=new Person("kumar")
var specificCall=function(){
console.log("Dude "+ this.fullName);
}
var newGreeting=Person.call.bind(specificCall)// I don't understand the meaning here
newGreeting()// logs Dude
newGreeting(kumar)//logs Dude kumar How is this happening
I think Person.call is a function as I have checked typeof Person.call and specificCall is a function again. How can I bind a function to a function?
Though I wrote this code accidentally I don't understand the logic?
Person.call fires the constructor function whereas newGreeting fires the specificCall function. if you look closely newGreeting(kumar) is equivalent to specificCall.apply(kumar). Here the not only context of the scope is changed but the whole function itself. Its like specificCall is bound to Person.call?
Call allows you to set the scope for a functions exection, defaulting to window, while activating it.
//Set global variable to fallback to
var fullname = "Window";
//Set local variable
var person = {
fullname: "Bob"
};
//Definer caller object
function logObject() {
//"this" references current scope
console.log(this.fullname);
}
//Call with nothing, defaulting to global
logObject.call();
//Call with specific object as scope
logObject.call(person);
Bind sets the scope for a function, but doesn't activate it.
//Set global variable to fallback to
var fullname = "Window";
//Set local variable
var person = {
fullname: "Bob"
};
//Definer caller object
function logObject() {
//"this" references current scope
console.log(this.fullname);
}
//Bind with nothing, defaulting to global
var logger1 = logObject.bind();
//Execute
logger1();
//bind with specific object as scope
var logger2 = logObject.bind(person);
//Execute
logger2();
As far as i can tell, you bind specificCall to a call on Person, meaning that firing newGreeting is the same as running Person.call but with specificCall as the bound scope.
It's not pretty code and i would personally recommend a more readable structure:
var fullName = "window";
var Person = function (name) {
this.fullName = name;
};
var chandra = new Person("chandrakumar");
var kumar = new Person("kumar");
function specificCall(scope) {
if (scope === void 0) { scope = window; }
console.log("Dude " + scope.fullName);
}
specificCall(); // logs Dude window
specificCall(kumar); //logs Dude kumar

Variable not holding reference to `this`

function test1() {
this.name = 'test1';
var that = this;
function test2() {
this.name = 'test2';
console.log(that.name);
}
test2();
}
test1();
When this executes, I expect the console to log out test1. Why do I get test2 instead? I expect the that variable to hold a reference to the test1 function.
Your variable that becomes an object reference since you assign keyword this to it. That means the variable that will be an object and it will have a reference to this(that is, the current object).
Further, the variable that is not a value type. It is an object.
For more on this, search on " value type vs reference types ".
Hope this helps.
In a function definition, this refers to the "owner" of the function.
Please find the description in inline comments.
Rearranging your code according to the order of execution,
test1(); //Now this is called by window object
function test1() {
this.name = 'test1'; //window.name = 'test1';
var that = this; //that is also now window obj
test2(); //this.test2 or window.test2()
function test2() {
this.name = 'test2'; //window.name is now test 2
console.log(that.name); //since in third step, that is window, that.name or window.name is now test 2
}
}

Trying to understand scoping within two short JavaScript functions

What is the difference between the following two JavaScript functions? I know variables declared with var are local inside the function, and if declared withthis` keyword are exposed to outer word. is there any other difference between
function student(param1, param2, param3) {
this.name = param1;
this.age = param2;
this.address = param3;
}
and
function student(param1, param2, param3) {
var name = param1;
var age = param2;
var address = param3;
}
Short answer: You would use the first one for a constructor. The second function does nothing. If you want to use 'private variables', refer to functional scoped variables in the contstructor by instance methods via a closure.
This function would be used the following way to create a student. The parameters passed in are assigned to the newly create student object.
function student(param1, param2, param3){
this.name = param1;
this.age = param2;
this.address = param3;
}
var student1 = new student('steve', 22,'333 E 3rd Ave');
var student2 = new student('rachel', 34,'111 N 1st St');
console.log(student1.name); // 'steve'
console.log(student2.name); // 'rachel'
The second one wouldn't satisfy for a constructor.
It declares variables with functional scope that remain unused. Once the execution of the student function ends, the 3 variables defined within will be garbage collected. This function doesnt seem to accomplish anything.
What do you expect this function to do? It can't be used the same way:
function student(param1, param2, param3){
var name = param1;
var age = param2;
var address = param3;
}
var badStudent = new student('Greg', 22,'222 W 2nd Rd');
console.log(badStudent.name); // undefined
Edit
Someone brought up how to make 'private member variables' using variables declared with var inside a constructor. Use closures:
function student(param1, param2, param3) {
var name = param1;
var age = param2;
var address = param3;
this.getName = function(newName) {
if (newName)
name = newName;
return name;
};
}
var myStudent = new student('steve', 22,'333 E 3rd Ave');
console.log(myStudent.name);
console.log(myStudent.getName());
console.log(myStudent.getName('dan'));
console.log(myStudent.getName());
Note that because the instance method getName refers to a functional scoped variable declared in the constructor, a closure remains that has references those variables. Because of the closure there are still references to the variables once the constructor ends, and they are not garbage collected. Thus via the instance method, you can get and set this variable which cannot be accessed via the resulting object of the constructor.
Basic difference between this. vs var variables is the scope. Variables declared as a part of this will be part of objects and variables declared with var might be private. Might because, it depends on your return. If you do not use return, then they will be private
Sample
function Student(fname, lname, dob) {
var _fname = fname,
_lname = lname,
_dob = new Date(dob);
this.fullName = _fname + " " + _lname;
this.age = (new Date()).getFullYear() - _dob.getFullYear();
}
var stu = new Student('foo', 'bar', '1998/11/13');
console.log(stu);
console.log(stu._fname)
As you see, _fname is passed and was stored using var. So its scope is till function only. So when you try to access it outside function, it is not available.
So in simple, you can use this to define public properties and use var to define private ones.
In JavaScript for creating object we use 'function' as constructor, this constructor functions basically return an object .
when you declare variable with 'var' instants of 'this.var name' , in this case mean you try using those variable to create an object .those variable declared with 'var' just are local variable inside function.
on other hand , when you use 'this.variableName' you create a property for object that constructor function try to create it .
'this.' refer to object that constructor function create.
'var variableName' is just a local variable and it is not a property of 'this' object.
function student(param1,param2,param3){
this.name=param1;
this.age=param2;
this.address=param3;
}
var t=new student('farhad',28,'address');
will create this object:
t{
name:'farhad',
age:28,
address:'address'
}
and
function student2(param1,param2,param3){
var name=param1;
var age=param2;
var address=param3;
}
var t2=new student2('farhad',28,'address');
will create this object:
t2{
}
in 't2' you don't see any property
this is used inside a function and it contains the value of the object that invokes function
Here this refers to the instance of the object & is not assigned a value until an object invokes the function where it is defined
function Student(param1, param2, param3) {
this.name = param1;
this.age = param2;
this.address = param3;
this.print = function() {
console.log(this.name + this.age + this.address)
}
}
var stud = new Student('a', 'b', 'c');
stud.print(); // abc
In the later case var name=param1;var age=param2;var address=param3; you are assigning each of the parameters to a variable and this variables have scope only inside the function

Javascript bind to object

function Developer(skill) {
this.skill = skill;
this.says = function() {
alert(this.skill + ' rocks!');
}
}
var john = new Developer('Ruby');
var func = john.says;
func();
I tried this example when I do this I get the following message undefined rocks! instead of Ruby rocks!.
can u explain why is that.
Function Execution Context and the this Keyword
JavaScript functions have an execution context at invocation time such that the this keyword is bound to the object they are invoked from. If you call john.says() the execution context of the function would have a this keyword that points to john. If you instead assign a global variable func to the method says found on the object john you have changed the execution context to the global object. When you invoke the func function, this dereferences to window (or undefined*) and since window.skill is undefined, says will coerce that value into a string to concatenate it with the string ' rocks!'.
How to guarantee execution context using bind
You can instead bind a copy of the function to an object (effectively locking it's context reference):
var func = john.says.bind(john);
How to guarantee execution context using a closure
Alternately you can close over the relevant bits by using a closure in your constructor:
function Developer(skill){
var _this = this; // we keep a reference here
this.skill = skill;
this.says = function(){
alert(_this.skill + ' rocks!');
// when invoked _this refers to the context at construction
}
return this;
}
How to guarantee a value using a closure
You could just reference the skill value directly from the method and so not need the context at all:
function Developer(skill){
// because skill is defined in this context, says will refer to this context
// to get the value of the skill variable.
this.says = function(){
alert(skill + ' rocks!');
}
}
How to guarantee an execution context at invocation time using call and apply
The final options are to to invoke the method with the context you want at invocation time:
func.call(john /*, optional arguments... */);
func.apply(john /*, optional arguments as an array */);
How to use prototypes to allow the dynamic execution context to set the right this
If we want to reuse a method between object instances or types but have the right execution context when invoked we can use the prototype property.
function Developer(skill){
this.skill = skill;
this.says();
}
Developer.prototype.says = function(){
alert(this.skill + ' rocks!');
}
var john = new Developer("Ruby"); // alert("Ruby rocks!")
var tony = new Developer("JavaScript"); // alert("JavaScript rocks!")
More reading:
Specification for execution contexts: http://ecma-international.org/ecma-262/5.1/#sec-10.3
MDN Docs about this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this?redirectlocale=en-US&redirectslug=JavaScript%2FReference%2FOperators%2Fthis
* "use strict" activates a special strict mode representing the future of JavaScript. This special strict executing environment will not resolve to the global object when a context has not been set, instead resolving this to the appropriately scoped value undefined.
Let's illustrate what is happening, by actually returning the object:
function Developer(skill) {
this.skill = skill;
this.says = function() {
alert(this.skill + ' rocks!');
}
return this; //this is basically { skill: skill, says: function() {} }
}
var john = new Developer('Ruby');
var func = john.says; // all it knows is the function() part, has no knowledge of skill
func(); //undefined rocks!
Ok, so why are we getting undefined? Well, once we rip out the function, it no longer has context - it doesn't know what this is. If we were to do:
func.apply(john);
Then we are passing in John as the this parameter. A workaround, it to pass the value in to the function when we create it:
function Developer(skill) {
this.skill = skill;
this.says = function(skill) { //<--calling this function
return function() { //<--returning this function
alert(skill + ' rocks!');
};
}(this.skill); //<--passing in the value here
return this;
}
When you call a function, this will refer to the object on which the function was called.
When you say
var func = john.says;
you are just getting the function object (it doesn't remember the object on which it is defined).
When you invoke the function, like this
func();
there is no current object, on which func is invoked. So, by default, JavaScript assigns the global object(window object in browser) as this. Since, skill is not defined in the global object, undefined is returned.
Note: In Strict mode, this will be undefined, if there is no current object.
That is why we have to explicitly bind the object to the function, like this
func.bind(john)();
Function.prototype.bind will return a new function, which is bound to john. So, when you invoke this this will refer john.
Because func() only has the function and not any other related info.(this.skill)
The context is lost. this referred to the john when the says() is called.
But now, when you call func() this refers to the window. Therefore this.skills will return undefined unless you have a global variable of the same name.
the fastest way to keep the above pattern is to bind this direct to the method this.says = function () { ... }.bind(this);:
function Developer(skill) {
this.skill = skill;
this.says = function () {
alert(this.skill + ' rocks!');
}.bind(this);
}
var john = new Developer('Ruby');
var func = john.says;
func(); // Ruby rocks!
try this
function Developer(skill) {
this.skill = skill;
this.says = function() {
alert(this.skill + ' rocks!');
}
}
var john = new Developer('Ruby');
john.says();
working demo
http://jsfiddle.net/ZENtL/

Scope of self-invocation function in Javascript

Why does self-invocation function inside a function don't get the scope of the outer function in JavaScript?
var prop = "global";
var hash = {
prop: "hash prop",
foo: function(){
console.log(this.prop);
(function bar(){
console.log(this.prop);
})();
}
};
var literal = {
prop: "object"
};
hash.foo();
// hash prop
// global
hash.foo.call(literal);
// object
// global
Looks like altering the scope of the outer function has no effect on the scope of the inner self-invocation function.
PS: The question is not about how to alter the scope of the inner function. But what is the proper explanation in the "Javascript language" perspective? Does all self executing functions have 'global' scope by default? If so, why?
Your problem is the this and what it references:
foo: function(){
console.log(this.prop);
(function bar(){
console.log(this.prop); <--- this does not reference to foo here, but instead it refers to the window object
})();
}
You need to keep a reference to the outer this:
foo: function(){
console.log(this.prop);
var that = this;
(function bar(){
console.log(that.prop); <--- tada!
})();
}
Update
Some explanation. It's all about how JavaScript determines the context when invoking a function.
function Test() {
this.name = "Test";
this.bar = function() { console.log("My name is: "+ this.name);}
}
function Blub() {
this.name = "Blub";
this.foo = function() { console.log("My name is: " + this.name);}
}
var a = new Test();
var b = new Blub();
// this works as expected
a.bar(); // My name is: Test
b.foo(); // My name is: Blub
// let's do something fun
a.foo = b.foo; // make an educated guess what that does...
a.foo() // My name is: Test
Huh? Aren't we referencing the method of Blub? No we're not. We are referencing the unbound function of Blub.
JavaScript binds on . (dots) and based on that it decides waht the value of this should be.
Since you're not calling your anonymous function on an object (therefore no .) it will make this reference to the global object, which is - in case of the browser - the window object.
Another example (one might think this would work):
var str = "Hello World";
var ord = str.charCodeAt; // let's make a little shortcut here.... bad idea
ord(0) // no dot...
Instead of the char codes that are in str we get the ones that are in the global object, of course that's not a string so charCodeAt calls toString on which results in "[object DOMWindow]"
You are not applying any object as the this context when you call the inner function, so it gets this set to window by default. If you wanted to call the closure with the same this as the outer function, you would have to do:
(function bar(){
console.log(this.prop);
}).call(this);
Or:
var that = this;
(function bar(){
console.log(that.prop);
})();

Categories