Can't call variable from another object - javascript

I have 2 objects: myObject1 and myObject2.
I am trying to call a private variable from myObject1 using my0bject2 method called increment, but console.log says NaN.
Is there any way to call myObject1 variable directly from myObject2 method? Maybe extend it somehow?
var myObject1 = function() {
var x = 0;
return{}
}();
var myObject2 = function() {
return{
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();

When you specify var x within myObject1 you are declaring that variable as private. The only thing that has access to that variable are methods within myObject1. As you noted, this variable is private.
You didn't make it clear if you wanted to keep it private or not so I will assume you just want to access it. You could do a couple of things here.
You could attach the variable as an object property of myObject1 via this. this refers to the current scope (myObject1) so by saying this.x within myObject1 you are saying myObject1.x. From that function, you will need to return this so that instances of it can access all of its public properties.
var myObject1 = function() {
this.x = 0;
return this;
}();
var myObject2 = function() {
return {
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
You could also just return the properties that you want, in this case x. You have more control over what is returned in this case and you get the same result as above.
var myObject1 = function() {
var x = 0;
return {
x: x,
};
}();
var myObject2 = function() {
return {
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
Either way, these methods both expose the x property of myObject1 making it public.

Its because you have to create an instance of the function myObject before you can access its properties,
/* Create an object called myObject which can be instantiated using the new keyword */
var myObject = function() {
/* use 'this.x' to define an internal variable for x */
this.x = 0
};
/* this is where you create a new instance of myObject which will have the 'x' property */
var myObject1 = new myObject();
var myObject2 = function() {
return {
increment: function() {
myObject1.x += 1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();
EDIT: Added comments to the code to explain the important modifications

myObject1 returns an object in which x is its own property. So now you're incrementing one of the property of the object. Earlier, with var x you were assuming to increment property of myObject1 but in fact it was just a local variable for that function which returned your object, i.e. declaring var x in function has no effect whatsoever.
var myObject1 = function() {
return{
x: 0
}
}();
var myObject2 = function() {
return{
increment: function() {
myObject1.x +=1;
console.log(myObject1.x);
}
}
}();
myObject2.increment();

In javascript, there is function scope, that is anything you define with var keyword is accessible within that function body.
Here you are implementing the module pattern. Your functions behave as constructors (or a factory) in your case (returning the object constructed). myObject2 cannot access x as it is defined in another scope and it is not included in the object generated by your first function.
You can return the x variable inside the return object;
var myObject1 = function() {
return{
x: 0
}
}();
That makes it a property on the returned object (that is assigned to myObject1) and you can access it.
You should study "function scope" and "module pattern" in more detail.

You're defining a variable in your functions scope. Instead, you should assign a variable to your current object (this) and of course return that object instead of an empty one.
var myObject1 = function() {
this.x = 0;
return this;
}();

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;

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.

How can I access the super of the super class in JS?

The code is pretty explanatory. What I am doing is wrong. How can I access the a property of the A object in the onclick event of an object declared inside of an A object method?
function A(){
this.a = 0;
};
A.prototype.myfun= function(){
var b = document.getElementsByClassName("myclassName");
b[0].onclick = function(e){
//How can I get the a property of the A object in here?
this.a = 1;
}
};
Could I somehow pass this as an argument like this?
b[0].onclick = function(e, this){
Since this in a function references to the function itself, you could do 2 things. Pass around the reference, or create a variable you won't overwrite that represents the this
function A(){
this.a = 0;
};
A.prototype.myfun= function(){
var self = this;
var b = document.getElementsByClassName("myclassName");
b[0].onclick = function(e){
self.a = 1;
}
};

Passing Scope from one function to another

I have an object litrel that has 2 anonymous functions defined as such:
obj = {
funcA : function(){
var a = 'a';
},
funcB : function(){
// but how do you access the scope in 'funcA' to access variable 'a'?
console.log(a)
}
}
I do not want to pass any variables just the scope of 'funcA' - thoughts?
JavaScript scope doesn't work like that because it has (only) function scope (except when using the let keyword).
What you could do is...
obj = {
a: 0,
funcA: function() {
this.a = 'a';
},
funcB: function() {
console.log(this.a);
}
};
jsFiddle.
You can use the a closure with the module pattern to get access to variables in an outer scope. More than one function can accesses the variable:
var obj = (function() {
var a;
return {
setA: function(value) {
a = value;
return a;
},
getA: function(value) {
return a;
},
multiplyA: function(value) {
a = a * value;
return a;
}
};
}());
alert( obj.setA(5) ); // 5
alert( obj.multiplyA(2) ); // 10
alert( obj.getA() ); // 10
See Douglas Crockford's article on Private Members in JavaScript. In this way, functions can share a common variable object on their scope chain. You still can't reference it or pass it to another function, but you might be able to get the functionality you want.
Does this code do what you want? If not, I'm really curious as to why.
obj = {
funcA : function(){
this.a = 'a';
alert(this.a);
},
funcB : function(){
// but how do you access the scope in 'funcA' to access variable 'a'?
alert(this.a);
}
}
obj.funcA();
obj.funcB();
What is the difference between accessing the actual scope of a separate function and setting members on the parent object as I have done?
Also, since there isn't a way of passing scope, are you just going to use something similar to this code instead?

Categories