Javascript immediately invoked function expression "this" - javascript

This seems to correctly return the correct value of 1:
let test = function() {
let test = 1;
function getTest() {
return test;
}
return {getTest};
}();
console.log(test.getTest());
But this does not:
let test = function() {
this.test = 1;
function getTest() {
return this.test;
}
return {getTest};
}();
console.log(test.getTest());
Why?

I am unsure what exactly you were trying to achieve with your code. An IIFE should not be mixed up with an object instantiation which I have demonstrated below. In this context the this works as expected:
const test=new function() { // object instantiation
this.test = 1; // this defines the property test
this.getTest=function(){
return this.test; // this references the same property
}
};
// test.test=123; // you can manipulate the property test here
console.log(test.getTest());
Or, in form of a simple object assignment:
const test={ // object assignment
test: 1, // defines the property `test`
getTest(){ return this.test; } // the `this` references the `test` property
};
console.log(test.getTest());

Because of the scope of this.
let test = function() {
this.test = 1; // this refers to the scope of the 1st level function
function getTest() {
return this.test; // this refers to the getTest function, and is undefined
}
return {getTest};
}();
console.log(test.getTest());
Just to make the point of the behavior of this you could use an arrow function, which captures the encompassing scope, including this
let test = function() {
this.test = 1;
const getTest = ()=> this.test; // still points to upper function
return {getTest};
}();
console.log(test.getTest());
But note that this a bad practice in this case because you keep in memory a reference to a function after it has reach end of scope. You shouldn't return any reference to this from a this kind of immediately invoked function, it creates a memory leak.
So a better thing to do would be to define the function and the test value on the returned object, with this being a reference to this object an not of a closure's scope object that doesn't exist any more
let test = function() {
return {
test: 1,
getTest() {
return this.test;
},
}
}();
console.log(test.getTest());

Related

JavaScript return by reference

I have a function that returns a variable and a function which edit the variable value.
But it's not working and I don't know why.
And what is the correct way to do this?
Here is my code:
function t(){
var test = 1;
var changeTest = function(){
test++;
}
return {
changeTest:changeTest,
test:test
}
}
var b = t();
b.changeTest();
console.log(b.test) // the value is 1 not 2
The value of test is a primitive and so it is indeed not a reference.
A solution: make the exposed test property a getter, so that the value of the private test variable is retrieved at the moment the property is accessed:
return {
changeTest:changeTest,
get test() { return test }
}
function t(){
var test = 1;
var changeTest = function(){
test++;
}
return {
changeTest,
get test() { return test }
}
}
var b = t();
b.changeTest();
console.log(b.test);
With this pattern, the test variable remains private, and you only expose the features you want to. So in this case you allow explicitly to read the test value, but not to write it.
NB: you can shorten the object literal notation to just:
return {
changeTest,
get test() { return test }
}
In changeTest you have to use this.test instead of test.
function t(){
var test = 1;
var changeTest = function(){
this.test++;
}
return {
changeTest:changeTest,
test:test
}
}
var b = t();
b.changeTest();
console.log(b.test) // the value is 1 not 2
If you would like to increase inside of the function, you need to use this keyword inside, just like the following:
function t(){
const changeTest = function() { this.test++; };
return { changeTest, test: 1 };
}
const b = t();
console.log('original value', b.test);
b.changeTest();
console.log('increased value', b.test);
The reason is why it is happening because you are not modifying the value inside, it is called lexical closure what you face with, as the documentations states:
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.
Using the this keyword the code accesses the proper test variable.
For further explanation please read further here:
Closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
I hope this helps!

How to keep 'this' in a class from within a javascript closure

I have read this answer and IIFE but I can't seem to find the correct solution to my problem.
I have a simple class here:
define(['jquery'], function($) {
// Need 'self' because someCallback() is being called with .call() and 'this' changes
var self;
function Foo(number) {
self = this;
this.someNumber = number;
}
Foo.prototype = {
someCallback: function () {
//Use self because 'this' changes to a DOM element
var num = self.someNumber;
//Do something with the num
return num * 2;
}
};
return Foo;
});
and someCallBack() is being called by a jQuery plugin using .call(). Because of this, the context changed, hence the use of the self variable.
However, this is wrong because:
define(['foo'], function(Foo) {
describe('context question', function () {
var foo1 = new Foo(1);
var foo2 = new Foo(2);
it('"this" should work', function () {
var call1 = foo1.someCallback.call(this); // 4
var call2 = foo2.someCallback.call(this); // 4
expect(call2).toBe(4); // Only works because it is 'new' last
expect(call1).toBe(2); // Fails because 'self' is taken from foo2
});
});
});
How exactly should I wrap the self variable to make this code work?
You could probably just use the revealing module pattern and declare it as a "global" variable (local to the module):
define(['jquery'], function($) {
var someNumber;
function Foo(number) {
someNumber = number;
}
Foo.prototype = {
someCallback: function () {
return someNumber * 2;
}
};
return Foo;
});
Two ways of calling an object method which stores its own this value include
Define the method as a nested function which references its this value in a closure which stores this value in a variable. The function defined could be anonymous or declared with a name but must be evaluated each time a class instance is created, so as to create a new Function object capturing different values of self in function scope.
Take a statically defined function object and bind its this value using bind. Bind creates a new wrapper function object each time it is called.
The first method looks like (without Jquery or Jasmine):
function Foo(number)
{ var self = this;
this.num = number;
this.someCallback = function() // method with new Foo object stored as self in function scope
{ // something with num:
return self.num * 2;
}
}
and the second method could look like
function Foo(number)
{ this.num = number
this.someCallback = this.someCallback.bind(this); // bind prototypical method as local method.
}
Foo.prototype = {
someCallback: function () {
// this value is bound by constructor;
//Do something with the num
return this.num * 2;
}
};

About javascript prototype

A strange problems about javascript prototype :
(function(w){
if(!w)
return;
var TestJS = function(){
};
TestJS.prototype = {
data:{},
initData:function(){
this.data={
val_name_1 : 1,
val_name_2 : 2,
val_name_3 : "hello-3"
};
console.log(this.data);
return this;
},
TestChildJS:{
initChild:function(){
console.log(TestJS);
console.log(TestJS.data);
console.log(new TestJS().data.val_name_1);
console.log(TestJS.data.val_name_1);
}
}
};
window.TestJS = new TestJS();
})(window);
why 'TestChildJS' can not get 'val_name_1'?
TestJS.initData();
console.log(TestJS.TestChildJS.initChild());
console pic
so I have to write my code like that:
(function(w){
if(!w)
return;
var TestJS = function(){
};
TestJS.prototype = {
data:{},
initData:function(){
this.data={
val_name_1 : 1,
val_name_2 : 2,
val_name_3 : "hello-3"
};
console.log(this.data);
this.TestChildJS.initParentData(this);
return this;
},
TestChildJS:{
parentData:{},
initParentData:function(parent){
this.parentData = parent.data;
return this;
},
initChild:function(){
console.log(this.parentData);
}
}
};
window.TestJS = new TestJS();
})(window);
How to use the first way can get the content of the second way?
why 'TestChildJS' can not get 'val_name_1'?
when:
TestJS.initData();
is run, it adds a data property to the TestJS object (the one assigned by window.TestJS = new TestJS()). That property isn't inherited by any other object.
When:
console.log(new TestJS().data.val_name_1);
is run, the object returned by new TestJS() has not had it's initData method called yet, so it doesn't have a data property and it doesn't inherit it from the constructor (because the property is directly on the constructor itself, not its prototype).
Note also that assigning a new object to this.data creates a property directly on the instance, so adding to this.data is modifying the instance's data object, not the one on the constructor's prototype.
The patterns in your code (especially the second one) seem unnecessarily convoluted.
It has to do with the scope of the IIFE. A variable declared inside a closure shadows any outer variable with the same name. Since after the IIFE executes you no longer have access to its scope, TempJS inside it will always be a function constructor -- not an instantiated object.
Consider this example:
var i;
var func = (function(){
i = 1;
return function() {
console.log(i)
};
})();
func(i); // 1
i = 2;
func(i); // 2
If I re-declare the i variable inside the closure, look what happens:
var i = 1;
var func = (function(){
var i = 1;
return function() {
console.log(i)
};
})();
func(i); // 1
i = 2;
func(i); // 1
So one solution to your problem would be to declare TestJS once before the IIFE.
var TestJS;
(function(w){
if(!w)
return;
TestJS = function(){
};
// ...
TestChildJS:{
initChild:function(){
console.log(TestJS.data.val_name_1);
}
// ...
window.TestJS = new TestJS();
})(window);
TestJS.initData();
console.log(TestJS.TestChildJS.initChild()); // 1
Notice that I removed console.log(new TestJS().data.val_name_1);. TestJS is no longer a constructor function, so that line will throw.
Another solution is to assign the empty function expression to window.TestJS inside the closure, instead of var TestJS. Doing so will not create a local TestJS name and will therefore prevent the ambiguity.

Why does this javascript object behave differently with and without a module pattern?

I have the following code with and without a module pattern. I have given the results right next to the execution. In the module pattern, I am able to change foo and set_inner, while in the function object (non-module), I can't change foo and set_inner.
module pattern:
var someObj = (function () {
var instance = {},
inner = 'some value';
instance.foo = 'blah';
instance.get_inner = function () {
return inner; };
instance.set_inner = function (s) {
inner = s; };
return instance; })();
someObj.get_inner();//some value
someObj.set_inner("kkkk");
someObj.get_inner();//kkk
someObj.foo;//blah
someObj.foo="ddd";
someObj.foo;//ddd
non-module:
var someObj = function () {
var instance = {},
inner = 'some value';
instance.foo = 'blah';
instance.get_inner = function () {
return inner; };
instance.set_inner = function (s) {
inner = s; };
return instance; };
someObj().get_inner();//some value
someObj().foo;//blah
someObj.foo="aaa";
someObj().foo;//blah
someObj().set_inner("kkk");
someObj().get_inner();//some value
Any help is much appreciated. Thanks!
Your "module" example creates a single object, referred to by instance. The anonymous function is immediately invoked, and returns that object. So someObj refers to instance.
Your "non-module" example creates a new object each time you invoke it. The anonymous function is not immediately invoked. Instead, it has to be called every time you want to use it.
It would behave the same way if you assigned the return value to a variable and referred to that, instead of repeatedly invoking someObj:
var obj = someObj();
obj.get_inner(); //some value
obj.foo; //blah
obj.foo="aaa";
obj.foo; //aaa
//etc...

Why this function is attached to global context

When I use var keyword to declare any variable it gets declared inside the enclosing scope. However in the code below, I have declared function c (inside an object method a.b) with var keyword and still this inside the function c is bound to the global object window. Why is this?
var a = {
b: function () {
var c = function () {
return this;
};
return c();
}
};
document.write(a.b()); //prints: [object Window]
The value of this is determined by context, not scope.
When you call a function without any context (context.func()) as you do there (c()), the default context is the default object (which is window in browsers) unless you are in strict mode (in which case it is undefined instead).
(There are exceptions to this rule, such as apply, call, bind, and new but none of them apply here).
Many people get confused by this. The value this depends on one of 4 methods of invocation.
However, functional invocation and method-invocation cause most of the confusion.
If a function is a member of an object, this is the object itself.
obj.someFunction(); //method invocation
If a function is called without context this is the global object (in 'strict mode' this is undefined.)
someFunction(); //functional invocation
The confusion occurs when a function is called within an object, but not as a member of the object as in anObject.testWithHelper(..);
var testForThis = function(isThis, message) {
//this can be confusing
if(this === isThis)
console.log("this is " + message);
else
console.log("this is NOT " + message);
};
//functional invocation
testForThis(this, "global"); //this is global
var anObject = {
test: testForThis, //I am a method
testWithHelper: function(isThis, message) {
//functional invocation
testForThis(isThis, message + " from helper");
}
};
//method invocation
anObject.test(anObject, "anObject"); //this is anObject
//method invocation followed by functional invocation
anObject.testWithHelper(anObject, "an object"); //this is NOT anObject from helper
Here is my JSFIDDLE
If you would like c to return a, you can use closure:
var a = {
b: function () {
var that = this;
var c = function () {
return that;
};
return c();
}
};
Or avoid this all together:
var getNewA = function() {
var newA = {};
newA.b = function() {
var c = function() {
return newA;
};
return c();
};
return newA;
};
var newA = getNewA();

Categories