My application is accessing a third party external JavaScript file that I cannot alter.
Within the file is an object defined similarly to as follows:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
}
})(window);
I need to be able access the object_1_var within the object, but I'm struggling to access it.
object_1.v // returns undefined
object_1.obj_1_func() // returns the value via console, but I need to assign it to a var.
I have tried extending the object using as follows: (Using jQuerys $.extend())
object_2 = (function(){
return {
obj_2_func: function() {
return object_1_var;
}
}
})(window);
$.extend(object_1, object_2);
var my_var = object_1.obj_2_func(); // returns 'Uncaught ReferenceError: object_1_var is not defined'
What can I do to be able to access object_1_var?
You will not be able to access the variable. It happens to be a private member. Private members of an object can be accessed only by its member functions.
Read this.
object_1_var is a lexically scoped local variable.
That means that it can't be accessed by extending object_1 outside of its original definition.
The only way it can be accessed is by adding functions within the original lexical scope in which it was declared:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
}
var_1: function(x) {
if (typeof x !== 'undefined') {
object_1_var = x;
} else {
return object_1_var;
}
}
}
})(window);
but since you can't modify object_1, you're out of luck, I'm afraid!
Make it public, like this:
object_1 = (function(){
var object_1_var = 'object_1_var_value';
return {
obj_1_func: function() {
console.log(object_1_var);
},
object_1_var: object_1_var
}
})(window);
EDIT
If unable to edit the javascript (such as in a third party library - sorry for omission) then you will not be able to have access to object_1_var as it's scope is local to the closure object_1.
What you are trying to accomplish is impossible in JS.
With the construction of object_1 the variable goes out of scope of that method. The reason why the logging function can access the variable is what we call 'a closure'.
Sadly, object_1_var isn't accessible in this example. The variable is defined as local to within that particular function - the only reason that the other functions can see it is because they are also defined within that function. This "closure scoping" is an interesting feature in JavaScript that you don't see very often elsewhere, but is the only real way of defining "private" variables in JavaScript Objects.
Hope that helps!
In a worst case scenario, in the past I've worked around this sort of issue by effectively overwriting the definition of an object that was previously defined elsewhere - mainly in Greasemonkey scripts - but I wouldn't condone this for production uses!
The trick here is to just copy the entire piece of script into your own. It's ugly as hell, but it might just work! (YMMV)
Related
This is most likely a very simple one.
I have created a game in canvas and js. However I stumbled upon a problem. As I can access the variables and code inside the chrome console, I can change f.ex. the player score to whatever I'd like. This is not very good.
Is there some way I can make the file and variables non accessible through the console for players to use?
Thanks!
You can put the whole script into an IIFE so that any variables declared will be scoped to the just-invoked function, rather than be on the top level and accessible anywhere. For example:
(function() {
var playerName = 'bob';
var turnCount = 1;
// do stuff with playerName and turnCount,
// they won't be accessible or changeable from the outside
// you can declare functions and such here as well
// which will also only be accessible from the inside
})();
Like this, referencing playerName from the console will fail.
you can use a proxy.
javascript proxy
or you can make an IIFE and expose only the required functions such as:
var App = (function() {
var test = '';
var init = function() {
App.test = 'test';
};
return {
init: init
};
})();
or define your properties as not writable with Object.defineProperty()
I need to include a reference to JavaScript written by a third party on my website. Sadly, the developers that wrote this script decided to define all of their functions globally. You know, like this:
function AwesomeStringHelper() {
// ...
}
function MyGreatFunction() {
// ...
}
When I reference this script using a <script> tag, both of those methods will be added to the window object.
Since I prefer to not pollute the global scope, is there a way that I can change the scope of an external script? Ideally I'd like to be able to refer to these methods similar to ExternalLibrary.MyGreatFunction(), etc. I am not able to modify the third party script as it is hosted externally, and it changes frequently.
In the first instance, try to edumacate the third party developers on how to correctly write their modules.
If that doesn't work, do:
var ExternalLibrary = ExternalLibrary || window;
at the top of your code.
You can then use ExternalLibrary.MyGreatFunction() throughout to refer to their functions (even though they remain visible in the global window scope), and then later once the third party devs have fixed their scope issues then at most you need a one line change to maintain compatibility (or no change at all, if they happen to use the same ExternalLibrary name as you do).
Alternatively, use two simple snippets of code either side of the <script> tag which remember the keys of the window object, then move the newly appeared keys into a new object (at the same time deleting them from window):
Pre-load:
var ExternalLibrary = { _current: Object.keys(window) };
Post-load:
Object.keys(window).forEach(function(key) {
if (ExternalLibrary._current.indexOf(key) < 0) {
ExternalLibrary[key] = window[key];
delete window[key];
}
});
delete ExternalLibrary._current;
I've used a similar approach in the past (before strict mode was common) to check for leaking global variables.
If your third-party module assigns to the window object directly (like window.myGlobal = someValue), and you are able to download the source code manually, you should be able to "wrap" the entire script in a function, where the window object has been overloaded:
function wrapModule(code) {
// create a "fake" window object that inherits from the global object
var fakeWindow = Object.create(window);
// create a function wrapping the code
// note that "window" is a parameter name in this function, shadowing
// the global object
var func = Function("window", code);
// call function
func.call(fakeWindow, fakeWindow);
// return fake window object
return fakeWindow;
}
// run code
const fakeWindow = wrapModule(`
var x = 0; // local variable (will not be exported)
y = 1; // global variable (will still be leaked)
window.z = 2; // assignment to window
this.w = 3; // assignment to this
`);
// check what variables are exposed
console.log('window.x', typeof x); // window.x undefined
console.log('window.y', typeof y); // window.y number
console.log('window.z', typeof z); // window.z undefined
console.log('window.w', typeof w); // window.w undefined
// check what variables are exposed in fakeWindow
console.log('fakeWindow.x', typeof fakeWindow.x); // fakeWindow.x undefined
console.log('fakeWindow.y', typeof fakeWindow.y); // fakeWindow.y number
console.log('fakeWindow.z', typeof fakeWindow.z); // fakeWindow.z number
console.log('fakeWindow.w', typeof fakeWindow.w); // fakeWindow.w number
Assuming you know the specific functions being defined, then after the script is loaded, would this not work?
const ThirdPartyLib = {AwesomeStringHelper, MyGreatFunction};
delete window.AwesomeStringHelper;
delete window.MyGreatFunction;
ThirdPartyLib.AwesomeStringHelper(haveFun);
You can wrap the entire script in a function and return an object with the "public" functions you want, it can be tedious and hard to maintain.
var myLib = function() {
//entire script
return {
functionA : functionA,
functionB : functionB,
//rest of functions
}
}
Or like this (inmediately invoked function)
(function(global) {
//entire script
myLib.functionA = functionA;
myLib.functionB = functionB;
//rest of fn
global.myLib = myLib;
})(window);
You could automate this using gulp, i'm not sure if there's a good plugin for this.
Not sure if jQuery is an option or if you care for it but I don't know how to write native JS AJAX calls so bear with me:
$(document).ready(function(){
$.ajax({
url: 'www.example.com/awesome_script.js', // get the contents of the external script
type: 'GET',
crossDomain: true,
dataType: 'html',
success: function(data){
// build our script tag and wrap the contents inside of a function call
var script = "<script>"
script+= "var callMe = function(call_func, var1, var2, var3){";
script+= data;
script+= "return typeof call_func === 'function' ? call_func(var1, var2, var3) : 'You trying to dynamically call a variable? idk how to do that.';";
script+= "};";
script+= "<\/script>";
// assuming this is legal then just append the custom script tag to the <body> :-)
$('body').append($(script)[0]);
// profit?
callMe('AwesomeStringHelper', 'some_var'); // this function accepts one parameter
callMe('MyGreatFunction'); // this function accepts no parameters
}
});
});
This question already has answers here:
Console access to Javascript variables local to the $(document).ready function
(8 answers)
Closed 6 years ago.
I'm using a shopping cart api to build an ecommerce website. The creators made an sdk and then you have to make your own .js file for some other functions.
While debugging I would insert a console.log(etc..) anywhere in my .js file so that I could debug object options and etc..
But I would like to be able to use the sdk as a live tool, so instead of having to edit my .js file with new console.log() lines, I'd rather just be able to type object.color_code and have the console output that string for the object color code. At the moment though it just gives me uncaught reference error, object is not defined.
I think this is because my custom .js file has all of it's script inside a $(function() { EVERYTHING }); SO, when I try to call anything in EVERYTHING from the console it says it's undefined, but if I just used console.log inside EVERYTHING it would work. So is there a way I can get around this?
Feel free to explain why it isn't working but I'd like a way to enable this, don't tell me there isn't a way, even if I have to prefix what I want with the .js file it's coming from each time, I don't mind
You were correct in that all of your variables inside the function are only being defined locally, and thus can't be accessed via the console. However, in Javascript there are at least two options for setting global variables from inside functions; If you use these to declare a variable you want to access from outside the function, it will work:
Assign a value to an undeclared variable: varname=value;
Assign the variable to the window object: window.varname=value; or window['varname']=value;
A possible workaround is to expose the object(s) that you want to debug in the global scope:
(function() {
var privateStuff = { foo: 'bar' };
// make privateStuff public for debugging purposes
window['debugObject'] = privateStuff;
})();
document.write(debugObject.foo);
If you want to expose several objects with rather common names that are likely to collide with existing ones, make sure to expose them within an object with an uncommon name rather than directly:
(function() {
var x = { str: 'this is' },
y = { str: 'a test' };
window['debugObject'] = {
x: x,
y: y
};
})();
document.write(debugObject.x.str + ' ' + debugObject.y.str);
If you're happy to change the source file then you could export whatever you want to access from EVERYTHING as a global.
$(function() {
//EVERYTHING
...
window.Ireply = window.Ireply || {};
window.Ireply.object = object;
...
});
console.log(Ireply.object); // some object
You can change a declaration like
$(function(){
var cart = {};
})
To
var cart;
$(function(){
cart = {}
})
Or
$(function(){
var cart = {};
window.cart = cart;
})
But you will want to avoid polluting global namespace. You will also want to be careful about using globals inside callbacks or loops where you can run into unexpected behaviors since local variables scope is often important to be kept local
this.func0 = function() {
console.log('hi')
}
this.func0() // this works
func0() // but not this?
Why func0() is not working?
Aren't this.f() and just f() both supposed to access global object?
The important thing to understand here is, in Node.js, each and every JavaScript file will be treated as a separate module.
So, within the JavaScript file, this will refer to the current module object, not the global object. We have something called global object in Node.js, which is somewhat similar to browsers' window object.
This is what you are doing with your program
this.func0 = function() {
console.log('hi')
}
you are creating a function object and assigning it to the current module's func0 attribute.
this.func0();
you are calling current module's func0.
func0();
JavaScript, searches for func0 in the current scope, then in the global scope but it doesn't find it anywhere. That is why it fails with the ReferenceError
ReferenceError: func0 is not defined
If you really wanted to set that in the global scope, then you should do it like this
global.func0 = function() {
console.log('hi')
}
func0();
// hi
This is a very old problem, but I cannot seem to get my head around the other solutions presented here.
I have an object
function ObjA() {
var a = 1;
this.methodA = function() {
alert(a);
}
}
which is instantiated like
var myObjA = new ObjA();
Later on, I assign my methodA as a handler function in an external Javascript Framework, which invokes it using the apply(...) method.
When the external framework executes my methodA, this belongs to the framework function invoking my method.
Since I cannot change how my method is called, how do I regain access to the private variable a?
My research tells me, that closures might be what I'm looking for.
You already have a closure. When methodA is called the access to a will work fine.
Object properties are a different thing to scopes. You're using scopes to implement something that behaves a bit like ‘private members’ in other languages, but a is a local variable in the parent scope, and not a member of myObjA (private or otherwise). Having a function like methodA retain access to the variables in its parent scope is what a ‘closure’ means.
Which scopes you can access is fixed: you can always access variables in your parent scopes however you're called back, and you can't call a function with different scopes to those it had when it was defined.
Since a is not a property of this, it doesn't matter that this is not preserved when calling you back. If you do need to get the correct this then yes, you will need some more work, either using another closure over myObjA itself:
onclick= function() { myObjA.methodA(); };
or using Function#bind:
onclick= myObjA.methodA.bind(myObjA);
yes, you're right. Instead of a method reference
var myObjA = new ObjA();
libraryCallback = myObjA.methodA
pass a closure
libraryCallback = function() { myObjA.methodA() }
If you are using jQuery javascript framework, easiest way is to use proxy:
$('a').click($.proxy(myObjA, 'methodA'));
I'd do this:
function ObjA() {
this.a = 1;
this.methodA = function() {
alert(this.a);
}
}
function bindMethod(f, o) {
return function(){
return f.apply(o, arguments);
}
}
var myObjA = new ObjA();
myObjA.methodA = bindMethod(myObjA.methodA, myObjA);
...
Where bindMethod binds the methodA method to always be a method of myObjA while still passing on any arguments which function() {myObjA.methodA()} doesn't do.