Variable scope in a module - javascript

I'm writing my first javascript module, and there is something that I don't understand with the scope variables. Here is my module :
var Module = (function () {
var myString ='a';
var changeString = function () {
myString ='b';
console.log(myString);
};
return {
changeString: changeString,
myString:myString
};
})();
Now if I do :
Module.myString; // returns 'a'
Module.changeString(); // returns 'b'
Module.myString; // returns 'a'
For me , the last command should return 'b' because I've changed myString with the changeString method. I don't understand why, since myString is declared outside the changeString method, so the scope seems ok. I would like to understand why it behaves like this, and how to make a method which overwrites the value of my variable.
Thanks in advance.

This makes a copy of myString at the time the object is created:
return {
changeString: changeString,
myString:myString
}
You can use a getter to return the local variable dynamically and that will give you the behaviour you expected:
return {
changeString: changeString,
get myString() {
return myString;
}
}
Full example:
var Module = (function () {
var myString ='a';
var changeString = function () {
myString ='b';
console.log(myString);
};
return {
changeString: changeString,
get myString() {
return myString;
}
}
})();
console.log( Module.myString );
Module.changeString();
console.log( Module.myString );

The anonymous function is only executed once, and the object returned by it is thus also only evaluated once: it is an object with a string property myString which is a primitive value -- it will not change if the variable myString is changed later.
If you wanted the behaviour like you expected it, then you should keep a reference in the Module to the object you return, and then mutate that object when the string property needs to change:
var Module = (function () {
var changeString = function () {
obj.myString = 'b'; // mutation of obj!
console.log(obj.myString);
};
var obj = {
changeString: changeString,
myString: 'a';
};
return obj; // we have a reference to this object
})();

As is said in other answers, because myString is a primitive, its value is copied from the scope to the member value of the anonymous object returned. In order to access the externally available copy of myString from the changeString() method, you can reference the anonymous object assigned to Module using the this keyword:
var Module = (function() {
var myString = 'a';
var changeString = function() {
// `Module.changeString()` causes `this === Module` inside here
this.myString = 'b';
};
return {
changeString: changeString,
myString: myString
};
})();
console.log(Module.myString); // returns 'a'
Module.changeString();
console.log(Module.myString); // returns 'b'
Using ES6 syntax, there are a few shortcuts you can take to simplify this approach:
var Module = (() => {
let myString = 'a';
return {
// ES6 for `myString: myString,`
myString,
// ES6 for `changeString: function () {`
changeString () {
this.myString = 'b';
}
};
})();
console.log(Module.myString); // returns 'a'
Module.changeString();
console.log(Module.myString); // returns 'b'

You see this behaviour because you return a fresh object.
The string myString gets copied because it is a primitive value.
You can achieve your goal with a few adjustments:
var Module = function() {
var myString = 'a';
this.changeString = function() {
myString = 'b';
};
this.getString = function() {
return myString;
};
};
var mod = new Module;
console.log(mod.getString()); // a
mod.changeString();
console.log(mod.getString()); // b

Related

Difference between variable and an arrow function [duplicate]

This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed last year.
What's the difference between a variable:
const a = console.log;
a('HELLO');
and an arrow function:
const a = (str) => console.log(str);
a('HELLO');
And why in case of using a class member it doesn't work
class MyClass {
foo;
str;
constructor(str, foo) {
this.str = str;
this.foo = foo;
}
getFoo() {
return this.foo(this.str);
}
}
const instance = new MyClass(123, console.log);
// works
const f = (str) => {
instance.getFoo(str);
}
f('Hello');
// doesn't works -> this.str is undefined
const f = instance.getFoo;
f('Hello');
variable = classInstance.function actually just returns a reference to the function in the prototype, not linked to the actual instance, the this.* looks for global variables.
If you want to be able to have another variable store the function with the context of that variable, you need to use .bind(...) or similar
You can also give it a custom context
class MyClass {
foo;
str;
constructor(str, foo) {
this.str = str;
this.foo = foo;
}
getFoo() {
return this.foo(this.str);
}
}
const instance = new MyClass(123, console.log);
// give it the context object
const f = instance.getFoo.bind(instance);
f('Hello');
const f2 = instance.getFoo.bind({ str: "abcd", foo: console.log});
f2("...");
// Equal to the prototype
console.log(instance.getFoo == MyClass.prototype.getFoo);

Declare variable to be function and have properties in one statement

Is it possible to combine the following into one statement:
// Combine from here
const foo = function() {
return 'hello';
}
foo.world = 'world';
// to here
console.log(foo() + foo.world) // helloworld
(foo = () => 'hello').world = 'world'
You can move the property declaration into the function.
let foo = function () {
foo.world = 'world';
return 'hello';
};
console.log(foo() + foo.world)
You have to assign statics outside of the function/class declaration. Here is an alternative ES5 version using classes.
class foo {
constructor() {
// constructor...
}
toString() {
return 'hello'
}
}
foo.world = 'world' // static member variables need to be assigned outside, no other way
console.log(new foo() + foo.world) // helloworld

Accessing properties in nested function returning another function

Is there a way to access properties in nested functions like that :
function func(){
this.new_func=function(){
console.log("something");
return 'something';
}
this.someValue=7;
return function(){
return "something_diff";
};
}
var obj=new func();
obj(); //works with returning "something diff"
obj.new_func(); // TypeError: obj.new_func is not a function
obj.someValue; // undefined
I need to delete whole "return function()..." part in order to access "someValue" and "new_func()". Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
When you have a constructor that returns an object, that object replaces whatever you assigned to this. So indeed, the members new_func and someValue are lost.
To combine the returned function together with the other members, you can do this:
function func() {
var f = function() {
return "something_diff";
};
f.new_func = function() {
console.log("something");
return 'something';
}
f.someValue = 7;
return f;
}
var obj = new func();
console.log(obj());
obj.new_func();
console.log('someValue:', obj.someValue);
You can do it like this:
var parentFunction = function() {
var nestedFunction = function() {
var value = "nestedValue";
var moreValues = "more values";
return {
value: value,
moreValues: moreValues
}
}
var anotherNestedFunction = function() {
var anotherValue = "nestedValue";
return anotherValue;
}
return {
nested: nestedFunction,
another: anotherNestedFunction
}
}
Then:
var newFunction = new parentFunction();
var nested = newFunction.nested();
console.log("nested value: ", nested.value);
console.log("another nested value: ", newFunction.another);
Here is a working example:
Why is it acting like that, and is there a way to somehow access that properties, while still returning another function ??
Because of the pharenteis:
var obj=new func();
Basically you're firing your function and what is stored the variable obj is what the "func" returns.
In order to access to private properties, you should look at the Closures: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

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...

Setting a variable in the closure scope

I think I understand why variables exist outside of the function they were declared in, because you're returning another function:
myFunction = function() {
var closure = 'closure scope'
return function() {
return closure;
}
}
A = myFunction(); // myFunction returns a function, not a value
B = A(); // A is a function, which when run, returns:
console.log(B); // 'closure scope'
The way that it's written now, calling A() is like a getter.
Q: How can I write myFunction so that calling A(123) is a setter?
Try the following:
myFunction = function() {
var closure = 'closure scope'
// value is optional
return function(value) {
// if it will be omitted
if(arguments.length == 0) {
// the method is a getter
return closure;
} else {
// otherwise a setter
closure = value;
// with fluid interface ;)
return this;
}
}
}
A = myFunction(); // myFunction returns a function, not a value
A(123); // set value
B = A(); // A is a function, which when run, returns:
console.log(B); // '123'
You could do something like this if you want both getter and setter for example:
var func = function() {
var closure = 'foo';
return {
get: function() { return closure; },
set: function(value) { closure = value; }
}
};
var A = func();
A.set('foobar');
console.log(A.get()); //=> "foobar"
Should be as simple as:
myFunction = function() {
var closure = 'closure scope'
return function(setTo) {
if (typeof setTo !== "undefined") {
closure = setTo;
return this; //support call chaining, good idea hek2mgl
} else {
return closure;
}
}
}
Since the closure variable is within the closure of the function's scope, you should be able to assign to it the same way you can read from it.
See jsFiddle: http://jsfiddle.net/WF4VT/1/
Another alternative would be to use a class and define getters and setters:
function MyClass(p){
this._prop = p;
}
MyClass.prototype = {
constructor: MyClass,
get prop(){
return this._prop;
},
set prop(p){
this._prop = p;
}
}
var myObject = new MyClass("TEST");
console.log(myObject.prop);
myObject.prop = "test";
console.log(myObject.prop);
Demo: http://jsfiddle.net/louisbros/bMkbE/
jsFiddle Demo
Have your returned function accept an argument. Use it as a setter:
myFunction = function() {
var closure = 'closure scope';
return function(val) {
closure = val;
return closure;
}
}
A = myFunction(); // myFunction returns a function, not a value
B = A(123); // A is a function, which when run, returns:
console.log(B); // 'closure scope'
Revisiting this question, I see that I could do it this way:
function outside() {
var result = 'initialized'
return inside
function inside(argVariable) {
if(arguments.length) {
result = argVariable
return this
} else {
return result
}
}
}
myFunction = outside() // outside returns a function
X = myFunction() // returns: 'initialized'
$('body').append(X + '<br>')
myFunction(123) // setter
X = myFunction() // returns: 123
$('body').append(X)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Categories