I am fairly new to Javascript and stomped across this while writing some frontend code. I reproduced this here:
var obj1 = {
func1(val) {
if (!this.obj2) {
this.obj2 = {
func2() {
console.log(val);
}
}
}
this.obj2.func2();
}
}
obj1.func1(10);
obj1.func1(20);
Why is the value of the val not getting changed inside the func2() method? It seems that only the value is getting copied to the object obj2 without taking the reference into consideration.
But it works perfectly when I change the code to this:
var obj1 = {
func1(val) {
this.val = val;
var that = this;
if (!this.obj2) {
this.obj2 = {
func2() {
console.log(that.val)
}
}
}
this.obj2.func2();
}
}
obj1.func1(10);
obj1.func1(20);
Can anyone tell the reason behind the difference? Is it something to do with the scope?
In the first snippet, when you do
if (!this.obj2) {
this.obj2 = {
func2() {
console.log(val);
}
}
}
The obj2 method that has been assigned, which gets assigned once and never again, closes over the val parameter in its scope when it was assigned - that is, when obj1.func1(10) was run.
In the second snippet, you assign to the val property of the instance every time func1 runs:
func1(val) {
this.val = val;
so by the time you log that.val later, it's always the parameter you just passed.
On the first case, you have:
if (!this.obj2) {
this.obj2 = {
func2() {
console.log(val);
}
}
}
So you make func2 always console.log the value it was originally created with. The second time it's called it will just call func2 with original val because functions retain access to the original scope they were declared (this is called closure).
The second time, when you do
this.val = val;
var that = this;
You're going to console.log a dynamic reference to this, so it's going to be value you passed because the this is going to be different on this scop
as far as I can understand the "This" keyword in javascript always points to the app itself or a global object. What you just did is setting a reference to the variable you are trying to use which is how I remedy this behaviour mistimes. I hope this helped at least?
have a look at this post. understanding the this keyword
cheers.
Related
I'm still confused when it comes to scope when working with object's methods. In the code below I have two neighbors. Each has it's own method.
var counter = 0;
var neighbor1 = {
name: 'John',
myFunction: function () {
console.log(this.name)
this.doStuff();
}
};
var neighbor2 = {
name: 'Bob',
doOtherStuff: function () {
console.log(this.name);
if(counter>5) {
return false;
} else {
counter++;
this.myFunction();
}
}
};
neighbor1.doStuff = neighbor2.doOtherStuff;
neighbor1.myFunction();
I understand when I call the neighbor1.myFunction method that it will call the neighbor2.doOtherStuff method because I've used the assignment operator above. What I don't understand is why 'this' will always be the neighbor1 object (John) and never the neighbor2 object (Bob). Can someone please explain in simple terms why neighbor1 is always 'this'? Thanks!
this is always referring/pointing to execution context(from where you executing the function, not from where you writing the function). You're invoking this.doStuff() at neighbor1 and hence the this is always pointing to neighbor1.
If you wish to understand more about this, I would recommend study this awesome material
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 6 years ago.
I am using Typescript for an Angular 2 project. I notice that when we use the keyword this inside a labmda expression vs a function, this refers to different things.
For example, let's say I have an Angular component like the following.
export class MyComponet {
isActive = true;
names = [ "john", "jeff", "jared" ];
doSomethingWithLambda() {
names.forEach( (value, index) => {
if(this.isActive) { //this.isActive refers to MyComponent.isActive
//do something...
}
});
}
doSomethingWithFunction() {
names.forEach( function(value, index) {
if(this.isActive) { //this.isActive is undefined, since this refers to the function
//do something
}
});
}
doSomethingWithFunction2() {
let isActive = this.isActive;
names.forEach( function(value, index) {
if(isActive) { //if we change isActive will this also change MyComponent.isActive?
//do something
}
});
}
}
What is really happening here (behind the scene, so to speak)? What's the magic behind this inside a lambda that makes it able to "correctly" refer to the outer class' fields? I understand this inside a function will refer to the function itself.
Also, I have a doSomethingWithFunction2 method that will reference MyComponent.isActive into a local variable. If I change that local variable, that should be like changing the one it references, right? (regardless of it being a "primitive" like integer/number or an "object" like JSON { })
The fat-arrow function syntax is shorthand for:
function () { }.bind(this);
bind is a Javascript ES5 method equivalent to this:
Function.prototype.bind = function bind(context) {
var func = this;
return function () {
return func.apply(context, arguments);
};
}
In regards to
Also, I have a doSomethingWithFunction2 method that will reference MyComponent.isActive into a local variable. If I change that local variable, that should be like changing the one it references, right? (regardless of it being a "primitive" like integer/number or an "object" like JSON { })
In Javascript, variables are like pointers and except for some limited cases (primitives and copy-on-write objects) will change the referenced value when mutated. Reassigning will not change the original value, e.g. isActive = false; but this.isActive = false would in fact re-assign the variable isActive on this which is now hopefully correctly assigned.
This has to do with how lambda function are implement in TS. this in arrow function is lexically inferred so it more in line with below code
function Person() {
var self = this; // Some choose that instead of self.
// Choose one and be consistent.
self.age = 0;
setInterval(function growUp() {
// The callback refers to the self variable of which
// the value is the expected object.
self.age++;
}, 1000);
}
So inside the lambda function it is not actually this but a context closer self. This may not be actual implementation but much closer to give you understanding of what is happening.
Now when you are outside the my impotent this refers to global var which would be window
The lambda function is similar to javascripts bind function.
Protip see your transpiled JS how your lambda function is transforming.
As you can see in the example below, I'm trying to wrap every function defined in obj so it gets to be able to be called with anotherObj as this, and then add that wrapper as a property to anotherObj.
Note: isFunction
var isFunction = function(x) {
return typeof(x) == "function";
}
for (prop in obj) {
if (isFunction(obj[prop])) {
var fn = obj[prop];
anotherObj[prop] = function() {
fn.call(anotherObj);
};
}
}
For some reason weird to me, every property now stored in anotherObj, references only the last property of the iteration.
However, if I use an external function like below, the references are ok.
var contextFn = function(fn, context){
return function() {
fn.call(context);
};
};
...
for (prop in obj) {
...
anotherObj[prop] = contextFn(fn, anotherObj);
...
}
Why is this happening? Is there something obvious I'm missing?
The (not-so-)obvious thing you're missing is that in your first loop, the variable "fn" is not local to the statement block it's declared in. Thus, it's shared by all the functions you're creating.
Your solution is in fact the correct one. By using a separate function, you're making copies of the values to be used in creating the actual wrapper functions, so that each wrapper will have it's own private copy of "fn".
I'm trying to create a function which returns another function. I want separate information when each of the inner function is run, but this isn't happening. I know that explanation is not great, so I've put together a small example.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return that;
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
This outputs 2, 2. What I would like is 2, 4 to be output. I know this isn't explained perfectly, so if it's not clear what I'm trying to achieve, can someone explain why the variable seems to be shared across the two functions?
Thanks
Like this ?
var testFn = function(testVal) {
var test = testVal
return {
getVal: function() {
return test
}
}
};
var ab = testFn (4)
var ac = testFn (2)
console.log(ab.getVal(),ac.getVal()) //4 //2
The problem in your code is this.getVal() / returning this
because 'this' refers to the global scope / Window
You are glubbering with the global namespace and overwriting Window.getVal() , the moment you are setting b = testFn (2)
This results in overwriting as method getVal too because they both refer to the global Object and always share the same method getVal
Therefore they share the same closure and are outputing 2
console.log("The same: " + (Window.a === Window.b)) // true
console.log("The same: " + (a === b)) // true
you can see that if you change it a little:
var testFn = function(testVal) {
var x = {}
return (function(testVal) {
var test = testVal;
x.getVal = function () {
return test;
}
return x
})(testVal);
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());//4 2
it suddenly works because it results in 2 different Objects returned (btw you don't even need the outer closure)
console.log("The same: " + (a === b)) // false
Here are the JSbins First / Second
I hope you understand this, I'm not good in explaining things
If theres anything left unclear, post a comment and I'll try to update the answer
This question comes down to the context in which functions are invoked in JavaScript.
A function that is invoked within another function is executed in the context of the global scope.
In your example, where you have this code:
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
})(testVal);
}
The inner function is being called on the global scope, so this refers to the global object. In JavaScript a function executed within another function is done so with its scope set to the global scope, not the scope of the function it exists within. This tends to trip developers up a fair bit (or at least, it does me!).
For argument's sake, lets presume this is in a browser, so hence this refers to the window object. This is why you get 2 logged twice, because the second time this runs, this.getVal overwrites the getVal method that was defined when you ran var a = testFn(4);.
JavaScript scopes at function level, so every function has its own scope:
var x = 3;
function foo() {
var x = 2;
console.log(x);
};
console.log(x); //gives us 3
foo(); // logs 2
So what you want to do is run that inner function in the context of the testFn function, not in the global scope. You can run a function with a specific context using the call method. I also recorded a screencast on call and apply which discusses this in greater detail. The basic usage of call is:
function foo() {...}.call(this);
That executes foo in the context of this. So, the first step is to make sure your inner function is called in the right context, the context of the testFn method.
var testFn = function(testVal) {
return (function(testVal) {
var test = testVal;
this.getVal = function() {
return test;
}
return this;
}.call(this, testVal);
}
The first parameter to call is the context, and any arguments following that are passed to the function as parameters. So now the inner function is being called in the right scope, it wont add getVal to the global scope, which is a step in the right direction :)
Next though you also need to make sure that every time you call testFn, you do so in a new scope, so you're not overwriting this.getVal when you call testFn for the second time. You can do this using the new keyword. This SO post on the new keyword is well worth reading. When you do var foo = new testFn() you create and execute a new instance of testFN, hereby creating a new scope. This SO question is also relevant.
All you now need to do is change your declaration of a and b to:
var a = new testFn(4);
var b = new testFn(2);
And now console.log(b.getVal(), a.getVal()); will give 2, 4 as desired.
I put a working example on JSBin which should help clear things up. Note how this example defines this.x globally and within the function, and see which ones get logged. Have a play with this and hopefully it might be of use.
The output you get is (2,2) because when you do
var that = this;
what you actually get is the global object (window),
the object that holds all the global methods and variables in your javascript code.
(Note that every variable that is not nested under an object or function is global and
every function that is not nested under an object is global, meaning that functions that are nested under a function are still global)
so, when you set:
var test = testVal;
this.getVal = function() {
return test;
}
you actually set the function "getVal" in the global object, and in the next run you will again set the same function - overriding the first.
To achieve the affect you wanted I would suggest creating and object and returning it in the inner function (as #Glutamat suggested before me):
var testFn = function(testVal) {
return new Object({
getVal: function() {
return testVal;
}
});
}
var a = testFn(4);
var b = testFn(2);
console.log(b.getVal(), a.getVal());
In this way, in the outer function we create an object with an inner function called "getVal" that returns the variable passed to the outer function (testVal).
Here's a JSBin if you want to play around with it
(thanks to #Glutamat for introducing this site, I never heard of it and it's really cool :D)
Is there any way to change a variable while out of scope? I know in general, you cannot, but I'm wondering if there are any tricks or overrides. For example, is there any way to make the following work:
function blah(){
var a = 1
}
a = 2;
alert(blah());
EDIT (for clarification):
The hypothetical scenario would be modifying a variable that is used in a setInterval function which is also out of scope and in an un-editable previous javascript file. It's a pretty wacky scenario, but it's the one I intend to ask about.
No. No tricks or overrides. You have to plan to have both places be able to see the variable in the same scope.
The only trick I can think of regarding scope is using window in a browser to get to the global object. This can help you get to a "hidden" variable--one that's in scope but whose name has been overtaken by a local variable (or other variable closer in the scope chain).
Closures and classes can afford you some other tricks with scope, but none that allow you to override the scoping rules entirely.
i don't see why you would need to do that, if you need a variable that is accessible from the outside, just declare it on the outside.
now, if you are asking this just because you are trying to learn something, good for you.
var a = 0;
function blah() {
a = 1;
return a;
}
a = 2;
alert(blah());
You can return the value from the function, of course:
function blah() {
var a=1;
return a;
}
But I assume that's not quite what you had in mind. Because a function invocation creates a closure over local variables, it's not generally possible to modify the values once the closure is created.
Objects are somewhat different, because they're reference values.
function blah1(v) {
setInterval(function() {
console.log("blah1 "+v);
}, 500);
}
function blah2(v) {
setInterval(function() {
console.log("blah2 "+v.a);
}, 500);
}
var a = 1;
var b = {a: 1};
blah1(a);
blah2(b);
setInterval(function() {
a++;
}, 2000);
setInterval(function() {
b.a++;
}, 2000);
If you run this in an environment with a console object, you'll see that the value reported in blah2 changes after 2 seconds, but blah1 just goes on using the same value for v.
Functions can access variables declared outside their scope, if they are declared before the function itself:
var a = 0;
function blah() {
a = 1;
}
a = 2;
alert(blah());
Note that your use of var a inside the function declared a local variable named a; here, we omit the keyword as otherwise it would hide a as declared in the outer scope!
No, that will never work, but you could use a global:
var a;
function blah(){
a = 1
}
a = 2;
alert(blah());
or use a closure:
function bleh() {
var a;
function blah(){
a = 1
}
a = 2;
alert(blah());
}
or you could pass it around with a return (which behaves differently, but probably is what you want to do):
function blah(a){
a = 1
return a;
}
a = 2;
alert(blah(a));