JavaScript, MooTools - Variable scope in Class/overwrite global variable - javascript

Can somebody explain me, why I am able to overwrite a method value of a global instance by just setting its value locally and why I am not able to do something similar with variables?
Is the only way to access the variable to use the window object hierarchy? Or is there maybe a shorter way?
(function() {
console.log(this);
var someVar = this.someVar = false;
var subClass = new Class({
test: false,
setValue: function(value) {
this.test = value
}
});
var subPub = this.subPub = new subClass();
var MainClass = new Class({
rewriteVar: function() {
console.log("someVar = " + someVar); // returns global value
console.log("subPub.test = " + subPub.test); // returns global value
someVar = true;
console.log("someVar local: " + someVar); // returns new local value
console.log("someVar global: " + window.someVar); // returns old global value
subPub.setValue(true);
console.log("subPub.test local: " + subPub.test); // returns new local value
console.log("subPub.test global: " + window.subPub.test) // returns new global value
}
});
/* var someObj = this.someObj = {};
var someVar = someObj.someMeth = false;
// And why is this possible?
var MainClass = new Class({
rewriteVar: function() {
someObj.someMeth = true;
console.log(window.someObj.someMeth); // returns new global value
}
}); */
window.addEvent("load", function() {
var test = new MainClass();
test.rewriteVar()
})
})()

This is to do with variable scope.
Javascript has functional scope.
So by doing:
var someVar = this.someVar = false;
You are declaring a local variable someVar and a global variable (which gets hoisted to the window object ie window.someVar), as this within your closure referes to the global scope ie window.
So when you write:
someVar = true;
You are overwriting the local variable with this new value.
Variables declared within a function definition are local to that function if you use the var key word:
(function () {
var name = 'Mark';
})();
// Out here you cannot access name
console.log(name);

(if i understood correctly the problem)
It has nothing to do with Mootools or the clases, #Felix_Kling already gave you the answer but I will ilustrate it with a simple example:
var aObj = bObj = {}; //since bObj is an 'object', aObj will store the objects reference
aObj.foo = "bar";
console.log(aObj.foo);
console.log(bObj.foo);
// output:
// "bar"
// "bar"
var a = b = 1; //since 'b' is primitive, 'a' will not store a reference of 'b', it will only copy it's value
a = 0;
console.log(a);
console.log(b);
// output:
// 0
// 1
i'm not really sure if this is what you were asking =) Hope this helps

Related

How can I detect when an Object is getting called in JavaScript

How do I detect when a JavaScript object is being accessed? I mean like a scope object, not a global object or a property of a object. I would like to be able to do something like this:
"use strict"
!function() {
var o = {
onaccess: function() {
console.log("o was accessed");
}
}
var x = o; //"o was accessed" when this happens.
}();
You can't that way as you are not accessing an object, but the reference to that object.
What you can try to do is to create a getter in the "global scope" (AKA window in browsers, for example) or to create an object with getters as properties, which you can think of "variables in objects".
(function() {
var _x = {};
Object.defineProperty(window, 'x', {
get: function() {
alert("access");
return _x;
}
});
document.getElementById("g").addEventListener("click", function() {
var a = x;
});
document.getElementById("w").addEventListener("click", function() {
var a = window.x;
});
})();
<button id="g">Access global x</button>
<button id="w">Access window x</button>
This code works because when you acces a variable (x in this case), it will run through the upper scopes until it finds an scope which contains the variable. It will finally look for the variable in the global scope, which is window, where I've set a getter that will run each time window.x is called (or simply x, which will end in accessing window.x internally).
This is hacky and I don't recomment it as you have to define a global variable. You can however try to create an object with getters in some properties (the same way as window.x), so you don't have to work with the global scope, but this way you have to always access the object property by prepending the object:
(function() {
var _x = {};
var prop = {};
Object.defineProperty(prop, 'x', {
get: function() {
alert("access");
return _x;
}
});
document.getElementById("w").addEventListener("click", function() {
var a = prop.x;
});
})();
<button id="w">Access property x</button>
Note that this will only work with predefined variable names. If you want to capture "all" variables, you can work with Proxies, which are impossible to hack over the global scope (window) but will work with objects, like this:
var h = {
get: function(target, name){
alert("Accessed property " + name);
}
};
var p = new Proxy({}, h);
document.getElementById("x").addEventListener("click", function() {
var a = p.x;
});
document.getElementById("y").addEventListener("click", function() {
var a = p.y;
});
document.getElementById("r").addEventListener("click", function() {
var a = p[Math.random()];
});
<button id="x">Access x property</button>
<button id="y">Access y property</button>
<button id="r">Access random property</button>
You can make a proxy, then you can log something everytime your function is called, be careful though Proxies are not 100% polyfillable in IE11 or less
function realFunction() {
return {
a: "5"
}
}
var proxy = new Proxy(realFunction, {
get: function(target, key) {
const obj = target();
console.log("key", `'${key}'`, "was called on", target);
return obj[key];
}
});
proxy.a // "5"

Instance variable is not returned are this param's considered private

Regarding private variable encapsulation - achieved by defining the private variable as var instead of it being a property of this ( not private this.private ). This is the most direct way of making a member private.
But, the above rule makes sense if the instance is being returned and used. In case we return a new object with get/set methods being exposed. Now, does storing the private variable in the this param still achieve private variable encapsulation
Is there any way that I can get access to the instance param when it's not being returned?
function X(init) {
this.private = init;
var that = this
function get() {
return that.private;
}
function set (tmp) {
that.private = tmp;
}
return {
get: get,
set: set
}
}
var tmp = new X(1);
console.log(tmp.get()) // 1
console.log(tmp instanceof X) // false
tmp.private = 20 // doesnt work as tmp isnt an instance object
console.log(x.get()) //1
x.set(20)
console.log(x.get()) //20
Do I have access to private as it's a property of this when the this isnt being returned?
No, we can not have access to private property on this as you are explicitly returning new object.
Consider this example when using a constructor:
function X()
{
// Scope of X()
var i = 1; // this variable is accessible only in this scope
this.get = function() { return i; };
}
a = new X(); // call the constructor
console.log(a.i); // this will be undefined
a.i = 2; // even if you try to set `i` on `a`
console.log(a.get()); // the actual `i` of `a` will still be 1
console.log(a.i); // this will be two.
Now for the example which uses a function which returns an object.
function Y()
{
// Scope of Y()
var i = 1; // this variable is accessible only in this scope
function get() { return i; };
return { // now you return an object which exposes a function that returns the value of `i` of this SCOPE.
get: get
}
}
b = Y();
console.log(b.i); // this will be undefined
console.log(b.get()); // this will be 1
b.i = 3; // this will work
console.log(b.get()); // this will still be 1
console.log(b.i); // this will be 3
I'm sorry I don't have enough time to continue the explanation, I'll return to this later.
You can hide(make private) member variables by hiding it in the scope.
Also, between these two approaches, only the first approach where you will be able to make use of the this keyword. You can't use this keyword in the second approach as its value will be the Window
You can achieve true privacy using a WeakMap.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap
WeakMaps allow you to create an affiliated object on which you can append any values.
You can't access them if you can't access the scope in which they were defined.
Using ES5 in a browser
"use strict";
{
const privacy = new WeakMap();
function X(init)
{
privacy.set(this, {});
const props = privacy.get(this);
props.init = init;
}
X.prototype.setInit = function(tmp)
{
privacy.get(this).init = tmp;
};
X.prototype.getInit = function()
{
return privacy.get(this).init;
};
window.X = X;
}
Using ES6 class notation in NodeJS
const privacy = new WeakMap();
class X
{
constructor(init)
{
privacy.set(this, {});
const props = privacy.get(this);
props.init = init;
}
setInit(tmp)
{
privacy.get(this).init = tmp;
}
getInit()
{
return privacy.get(this).init;
}
}
module.exports = X;

javascript function as a class not returning private variable

I have used javascript object as a namespace and defined a function as a property.Inside the function there are two property with reference to this parameter.One of the property is a function and other is a variable. Now when i called this function(defined as reference to this), i want to return the value of the second reference property(which is a variable). But inside the scope of this function, I am not able to get the value of the variable. Is something i am missing here?
Definition
var myNameSpace = myNameSpace || {};
myNameSpace.myClass = function(){
this.myVar = someValue;
var someConstantVar = 17.012;
this.myFunction = function(){
...
var returnValue = someConstantVar + ((this.myVar+22)*1.09);
return returnValue;
};
}
Usage
var testObject = new myNameSpace.myClass();
testObject.myVar = someNewValue;
var output = testObject.myFunction();
Now there is an error of undefined this.myVar(which is likely). So how do I get the value of this.myVar inside the function.
This should work for you:
var myNameSpace = myNameSpace || {};
myNameSpace.myClass = function() {
this.myVar = 3;
this.myFunction = function() {
return this.myVar;
};
}
var testObject = new myNameSpace.myClass();
var output = testObject.myFunction(); // 3
myVar is a field of every object that you create via new myNameSpace.myClass(). To access fields from inside the object, you have to use this.
Also, in this implementation, myVar is actually public, so you can access it directly like this:
output.myVar // 3
To make myVar a private variable, you can do this instead:
var myNameSpace = myNameSpace || {};
myNameSpace.myClass = function() {
var myVar = 3;
this.myFunction = function() {
return myVar;
};
}
You can do something like this.
var myNameSpace = myNameSpace || {};
myNameSpace.myClass = function(){
var that = this;
this.myVar = 'someValue';
this.myFunction = function(){
return that.myVar; //Here I need to return this.myVar
};
}
"this" inside "this.myFunction" is has scope only to "this.myFunction". You need scope of "myClass"

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.

Class loses "this" scope when calling prototype functions by reference

Can anyone explain to me why "b" returns undefined and how I can get around this problem? Why does the "this" scope get lost when I call prototype functions by reference?
MyClass = function(test) {
this.test = test;
}
MyClass.prototype.myfunc = function() {
return this.test;
}
var a = new MyClass('asd').myfunc();
var b = new MyClass('asd').myfunc;
// Returns "asd" correctly
console.log(a)
// Returns undefined??
console.log(b())
=== EDIT / SOLUTION ===
As plalx writes, the correct solution in my case is to use .bind(). So the result looks like this:
MyClass = function(test) {
this.test = test;
}
MyClass.prototype.myfunc = function() {
return this.test;
}
var a = new MyClass('asd').myfunc();
var b = new MyClass('asd'),
bfunc = b.myfunc.bind(b)
// Returns "asd" correctly
console.log(a)
// Also returns "asd" correctly!
console.log(bfunc())
You need to explicitely bind the this value if you want this behaviour.
var c = new MyClass('asd'),
b = c.myfunc.bind(c);
console.log(b());
By default, this will point to the leftSide.ofTheDot(); in an invocation, or simply the object on which the function was called.
Note: Calling b(); is the same as window.b();.
Binding every function to the object instance is possible but rather inefficient because functions will not get shared across instances anymore.
E.g.
function MyClass(someVal) {
var me = this;
me.someVal = someVal;
me.someFn = function () {
return me.someVal;
};
}
The line var b... is a function reference and you're not actually calling the function.
Here you are assigning to a variable a result of myfunc() function.
var a = new MyClass('asd').myfunc();
And here, you are asigning to b variable function reference, rather then runing it, and assign result to variable.
var b = new MyClass('asd').myfunc;
And finaly here:
console.log(b())
You trying to log result of function b and in that case b() isn't defined anywhere, only it is assigned reference to function.
Try this:
console.log(b); // It logs [Function]
But besides that, your question is hard to understand.
P.S. Use semicolons!

Categories