How can I detect when an Object is getting called in JavaScript - 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"

Related

How to pass function scope (not context)?

Can you please help me to find the error in the code? I need to pass one method's scope to another method.
var obj = {
doIt: function() {
var a = 10;
this.doThat.call(this);
},
doThat: function() {
console.log(a);
}
};
obj.doIt();
If you want to access a in the scope of context, you can try this.
var obj = {
doIt: function() {
this.a = 10;
this.doThat.call(this);
},
doThat: function() {
console.log(this.a);
}
};
obj.doIt();
You do pass the "context" (aka this) around. a however, is not part of the current context, but of the current scope. So you either move a into the context:
var obj = {
doIt: function() {
this.a = 10;
this.doThat();
},
doThat: function() {
console.log(this.a);
}
};
Or you directly pass the value of a into doThat:
//...
this.doThat(a);
doThat(a) { /*...*/ }
You need to understand that there is difference between context and scope. scope is going to be something which will dictate which variables are accessible from a particular site whereas context is what will be the value of this at particular site or inside a function.
so if you want to use value of a in function doThat you need to bind value of a with this which is context in function doIt like: this.a = 10; and then access it using console.log(this.a) inside function doThat

Can't call variable from another object

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;
}();

Why can't I reference an object literal from inside an object literal?

not sure if this is possible or if I'm doing it wrong. I'm trying to have an object literal reference one of it's own properties that is another object literal. I keep getting an error stating that it is undefined:
var MyObject = {
init: function(){
this.elements.buttons.click(function(){
alert('hit');
});
},
elements: {
buttons: $('button')
}
}
what am i doing wrong?
UPDATE:
main.js is calling init to initialize on demand which probably doofed the scope --
function executeFunctionByName(functionName, context /*, args */) {
var args = [].slice.call(arguments).splice(2);
var namespaces = functionName.split(".");
var func = namespaces.pop();
for(var i = 0; i < namespaces.length; i++) {
context = context[namespaces[i]];
}
return context[func].apply(this, args);
}
$(document).ready(function(){
// invoke init() for any element that requests it
$('[data-page]').each(function(i, p){
var page = 'APP.MODEL.' + $(p).data('page') + '.init';
executeFunctionByName(page, window)
});
});
If you are invoking that function directly without going through MyObject, this would be undefined. Replace this with MyObject and it should work.
This would not work as this would be defined to window:
var MyObject = {
// init: ...
}
var init = MyObject.init;
init();
See 3. Entering function code: How does the "this" keyword work?
If you wish to continue to use this, you may use Function.prototype.call() or Function.prototype.apply() like so:
var MyObject = {
// init: ...
}
var init = MyObject.init;
init.call(MyObject);

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();

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