I'm using Google's Closure Compiler in advanced mode, and I'm having a strange issue. Here is the uncompiled code, with returned log statement from the compiled version running:
goog.provide('frame.store');
goog.require('frame.storeBack.LocalStore');
goog.require('frame.storeBack.Mem');
frame.store = (function() {
/** prioritised list of backends **/
var backends = [
frame.storeBack.LocalStore,
frame.storeBack.Mem
];
frame.log(backends);
// [function rc(){}, function tc(){this.q={}}]
frame.log(frame.storeBack.LocalStore === backends[0]);
// true
frame.log(frame.storeBack.LocalStore.isAvailable === backends[0].isAvailable);
// false
frame.log(frame.storeBack.LocalStore.isAvailable);
// function sc(){try{return"localStorage"in window&&window.localStorage!==k}catch(a){return l}}
frame.log(backends[0].isAvailable);
// undefined
for (var i=0, len=backends.length; i<len; i++)
if (backends[i].isAvailable())
return new backends[i]();
// Uncaught TypeError: Object function rc(){} has no method 'Ga'
throw('no suitable storage backend');
})();
For some reason the static method isAvailable is not present when LocalStore is accessed via the backends array, and is present when it's accessed via it's global namespace.
Can anyone see why?
EDIT: for reference, here is the method declaration:
frame.storeBack.LocalStore.isAvailable = function() {
try {
return 'localStorage' in window && window['localStorage'] !== null;
}catch (e) {
return false;
}
};
Turn on --debug true to check your output and what frame.storeBack.LocalStore.isAvailable is renamed to.
Dump a variables name map to check whether frame.storeBack.LocalStore.isAvailable has been flattened.
For example, the Closure Compiler may flatten frame.storeBack.LocalStore.isAvailable first to frame$storeBack$LocalStore$isAvailable, then rename the whole thing to the global function "a" or something. This is called flattening of namespaces. Check the debug output to see whether your function declaration has been renamed to:
$frame$storeBack$LocalStore$isAvailable$$ = function() {
In such case, calling frame.storeBack.LocalStore.isAvailable() directly will still call the flattened global version, no prob here! However, you can't expact that isAvailable() exists in frame.storeBack.LocalStore (another object) any more. In the compiled output, frame.storeBack.LocalStore.isAvailable and frame.storeBack.LocalStore are now separated. This is the behavior of the compiler's namespace flattening, if it happens.
You're asking for trouble putting properties into a constructor function itself -- the compiler does a lot of optimizations on classes that you may not expect.
Check the debug output and variable names map to confirm. You may have to remove the closure wrapper function in order to see the actual names in the map file.
Not sure what your back ends are exactly...
But shouldn't you instantiate them?
var backends = { localStore : new frame.storeBack.LocalStore(),
mem: new frame.storeBack.Mem() };
Related
I am trying to detect when a function is created, preferable through a constructor. Functions are a type of object, right? So it makes sense that when you create a new one, it calls a constructor. Is there a way to override this, for example, something like this
var old = Function.constructor;
Function.constructor = () => {
alert('new function created!');
old();
};
function asdf() {}
var k = new Function();
If this is not possible, is there a way to get all currently defined functions? I am trying to trigger a piece of code on each function run.
You can't detect function creation.
Functions are a type of object, right?
Yes.
So it makes sense that when you create a new one, it calls a constructor.
No. Or - maybe, but that constructor is internal. Just like the construction of objects from array literals, object literals, regex literals, definition of a function directly creates a native object.
Is there a way to override this?
No. You'd need to hook into the JS engine itself for that.
If this is not possible, is there a way to get all currently defined functions?
No. At best, you could try the debugging API of the JS engine and get a heap snapshot, that should contain all function objects.
I am trying to trigger a piece of code on each function run.
Let me guess, that piece of code is a function itself?
Was able to get a semi-working attempt at this. It reads only global functions but it can add code to both the front and beginning of the function. Any tips on how to improve this, as I use classes a lot when I code?
Thanks to Barmar for the idea of looping through window properties, but since you can't access local functions and class functions, this may be the closest way to do this
<script>
function prepend(name) {
console.time(name);
}
function postpend(name) {
console.timeEnd(name);
}
var filter = ['prepend', 'postpend', 'caches'];
function laggyFunction() {
var l = 0;
while (l<1000) {l++}
}
var functions = [];
for (var property in window) {
try {
if (!filter.includes(property)) { // security error on accessing cache in stackoverflow editor along with maximum call stack size exceeded if prepend and postpend are included
if (typeof window[property] === 'function') {
window[property].original = window[property];
window[property].name = property;
window[property] = function() {
prepend(this.name);
console.log(this.original);
this.original.apply(null, arguments);
postpend(this.name);
}.bind(window[property]);
functions.push(property);
}
}
} catch(e) {
console.warn(`Couldn't access property: `+property+' | '+e);
}
}
document.write(functions); // functions the prepend and postpend are applied to
laggyFunction(); // test performance of the function
</script>
I'm writing this question after 2 days of total struggling during which I couldn't find a solution but also couldn't find an explanation of why this code is not working.
I will report a super simplified simulation of my code.
I have 5 Node.js files:
1) server.js -> Is the main file used to start the server
2) globalVars.js -> Is where all the server "global" objects are stored.
3) globalFunctions.js -> Is where all "general" functions are stored to be used by all other modules
4) helloManager.js -> Example file which contains some standard function used by the server
5) aspect.js -> Same as helloManager.js
//server.js
//Loading the globalVars file. All objects are passed by reference so I use this to store the global variables
var globalVars = require("./globalVars.js");
//Assigning to the respective global object all the functions exported from other modules
globalVars.Modules.globalFunctions = require("./globalFunctions.js");
globalVars.Modules.aspect = require("./aspect.js");
globalVars.Modules.helloManager = require("./helloManager.js");
//After this point, the objects in globalVars.js will be populated with the respective functions exported from the files
//A timeout just to be sure it is not a problem of timing? (Well, it is not...)
setTimeout(function(){
console.log(globalVars.Modules.helloManager.helloOutput());
}, 2000);
/*
Console will throw the following error:
../globalFunctions.js:6
return "MR. " + aspect.getAspect();
^
TypeError: aspect.getAspect is not a function
*/
//globalVars.js
//Objects that will be populated with the functions inside other modules
module.exports.Modules = {
aspect: {},
helloManager: {},
globalFunctions: {}
};
//helloManager.js
var globalVars = require("./globalVars.js");
var { globalFunctions } = globalVars.Modules;
module.exports.helloOutput = function(){
return "hello " + globalFunctions.getHumanData();
};
//aspect.js
module.exports.getAspect = function(){
return "human";
};
//globalFunctions.js
var globalVars = require("./globalVars.js");
var { aspect } = globalVars.Modules;
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
Please don't answer me to put everything in the same file, because my code is way more complicated to report so here I'm posting this very simple simulation.
I know that objects are assigned by reference and so if all modules get the variables from "globalVars" they works kinda like "global".
The problem is when in globalFunctions.js I load
var { aspect } = globalVars.Modules;
Since in server.js the module aspect.js is not loaded yet, it will be an empty object.
But I'm expecting that
var { aspect } = globalVars.Modules;
is getting the reference of globalVars and not a copy, so when server.js finishes loading all the modules, the variabile aspect inside globalVars.Modules will point to the correct object and so it would find the function I need!
Infact the console.log inside server.js is executed after all modules have been loaded for this exact reason.
Does anyone know what is the reason of this problem and how could I solve it?
Thank to everyone who will help!
What's happening
It's an issue of what it means to do const { aspect } = globalVars.Modules; (which is the same as const aspect = globalVars.Modules.aspect;). That is, it's a matter of assignment semantics.
Let's look at a simpler example, then we can see hot it applies to what you're doing. Assume you have:
let a = {/*original object*/};
When you do
b = a;
the value in a is copied into b. That value is an object reference, so they now both point to the same object, but there is no ongoing link between a and b. If you then do
a = {/*new object*/};
that has no effect whatsoever on b, because there's no ongoing link between a (the variable) and b (the variable). b still refers to the original object, not the new one.
The same is true of any assignable item, such as an object property or parameter. That's what's happening with globalVars.Modules.aspect. globalFunctions is grabbing the value (simple assignment semantics, though using destructuring), then server.js is replacing that value with a new one.
Here's how that's happening in your code:
// server.js
var globalVars = (function() {
// globalVars.js
return { // this is the `exports` object
Modules: {
aspect: {}, // *** That's the `a = {/*original object*/}`
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions = (function() {
// globalFunctions.js
const { aspect } = globalVars.Modules; // **** That's the `b = a`
return { // this is the `exports` object
getHumanData: function(){
return "MR. " + aspect.getAspect();
}
};
})();
// back in server.js
globalVars.Modules.aspect = (function() { // *** that's the `a = {/*new object*/}
return { // this is the `exports` object
getAspect: function(){
return "human";
}
};
})();
// back in server.js
globalVars.Modules.globalFunctions.getHumanData(); // Fails because the object it's using
// is the old one, not the new one
How to fix it
globalFunctions.js relies on aspect.js, so have it rely on it directly:
// In `globalFunctions.js`
const aspect = require("./aspect.js");
module.exports.getHumanData = function(){
return "MR. " + aspect.getAspect();
};
Assuming there are no cycles, that'll work.
At a larger level: There may be no reason to have globalVars.Modules at all. A module is only loaded once (normally), so rather than globalVars.Modules, just have each module directly rely on the module it needs, rather than funnelling it all through a central object. Node.js' module cache is already the central object.
If you don't want globalFunctions.js to rely on aspect.js directly (why not?), then don't copy the aspect property from Modules, use it as of when you need it:
// In `globalFunctions.js`
const {Modules} = require("./globalVars.js");
module.exports.getHumanData = function(){
return "MR. " + Modules.aspect.getAspect();
};
That'll work assuming nothing reassigns Modules (which nothing seems to in the code you've shown). But again, it makes more sense to rely on aspect.js directly if you can.
It's kind of fun to note that this is one of the reasons that modern ESM modules don't use simple assignment semantics like CommonJS ones do. ESM wouldn't help your specific thing, because you're using your own globalVars.Modules object instead of using the module objects, but it solves a problem that people often had with CommonJS modules which, like your problem, was caused by expecting b (an imported value) to be affected when reassigning a (the exported value). The issue people would have with CommonJS happened mostly when there were cycles between two modules (circular dependencies, directly or indirectly). ESM solves this by making the imported binding (b in my example) a live binding to the exported binding (a in my example). This is the only place JavaScript has what you could argue is a form of pass-by-reference (references to variables).
As you've noticed, var { aspect } = globalVars.Modules; copies the current value of globalVars.Modules.aspect to the local variable aspect. It's just alternative syntax for var aspect = globalVars.Modules.aspect.
If you later change the value of globalVars.Modules.aspect to a new object (as opposed to mutating the object that is already there) then the local variable doesn't update.
If you want the most recent value then you need to continue accessing globalVars.Modules.aspect whenever you need it.
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
}
});
});
I have a problem. I have defined some global variables and namespaced it into an object called "app".
Example:
window.app : {
foo : null,
bar : null,
}
Well, the idea is that I want to be able to modify those variables from any module by calling app.foo = "baz" or app.bar = "baz", but I don't want the user to be able to modify those variables from the browser console (element inspector).
Is it possible?
PD: Well, I have a Backbone.js collection which is sinchronized with the server. I don't want the user to be able to modify that collection with the console
No. The browser is the user's domain. They have the possibility to modify your scripts and inject their own functionality in various ways (through the console or browser plug-ins). That's one of the reasons why you should never blindly trust user input on the server side.
They could even manually forge a complete request, tricking your server into thinking that your JavaScript code made that request.
If you want these values to be secure, you need to keep them on the server. You can send them to the client, of course, as long as you keep a possibility to validate the values against those on the server.
The only way to make the variables not (easily) modifiable by a user is to remove them from global scope - something like
!function() {
foo = null;
bar = null;
}()
You'll need to redesign the way your modules interact with each other to accomplish this. An MVC Framework like Angular.js will help.
You should never rely on this as a security mechanism, though - the browser is fully in the user's control.
Still for them who are searching solution to this problem, use const modifier while assigning variable instead of var. Now try to change value of variable from browser console. It will throw error Uncaught TypeError: Assignment to constant variable that will prevent your data from being modified.
A possible way to avoid to (easily) modify javascript variables from the browser console is to either use the get operator (ECMAScript 5) or a getter-function.
To make it possible to define "private" variables, an anonymous function defines the variables in the local scope, so that it is not globally available. (as mentioned in joews' answer)
As mentioned before, this does not make it impossible to manipulate the variables.
Via get operator:
window.app = (function () {
var _foo = 123; // private variable
return {
get foo () { return _foo; }
};
}());
// --- accessing app from the console ---
// app.foo is readable from console, but not modifiable
console.log(app.foo);
app.foo = 234;
console.log(app.foo); // 123
// However, app.foo can still be modified via Object.defineProperty or
// removed with the delete operator
Via getter-function (older browsers, e.g IE < 9):
window.app = (function () {
var _foo = 123; // private variable
return {
foo: function() { return _foo; }
};
}());
// --- accessing app from the console ---
console.log(app.foo()); // 123
// However, the foo function can still be overwritten.
// But at least, the internal _foo variable is unaffected.
app.foo = function () { return 234; }
I currently have a Web Application that runs off a global Javascript-based API, and it is initialized like this:
var Api = {
someVar: "test",
someFunction: function() {
return "foo";
}
}
This API is shared across many "Widgets" that live in the Web Application, and they should all run off this single Api instance so they can pass data to each other.
AJAX is currently used to load these Widgets, for example in widgets/mywidget.html, and it's placed in, say, <div id='widget_<random number>'>...</div>
Certain other parts of the code may choose to add more functionality to Api, and it's currently done like this:
Api.myExtension = {
myNewFunction: function() {
return "bar";
}
}
However, some issues arise from this kind of usage:
Problem One: What if one Widget (these may be provided by third-parties) decides to hide some code within, and does something similar to Api = {}, destroying the global Api var everything lives on, and breaking the whole Application? Is it possible to protect this Api variable from being overwritten from outside? Only "extending" is allowed (adding new things), but "removing/changing" is not allowed. i.e.:
Api.foo = { test: "bar" } // allowed
Api.someVar = "changing the existing someVar"; // not allowed
The following code is located "inside" Api, for example:
var Api = {
Debug: {
Messages = new Array,
Write: function() {
Api.Debug.Messages.push("test"); // allowed
}
}
}
Api.Debug.Messages.push("test 2"); // not allowed
Probable Solutions I've Thought Of:
Suppose we simply use frames to resolve this issue. The Apis provided are now separate from each other. However, there's additional overhead when loading Api again and again if I have many Widgets running, and they can no longer communicate with the "Host" of the widgets (the page where frames reside in), for example, I may want to tell the host to show a notification: Api.Notify.Show("Test"), but it cannot do so because this Api is completely independent from other instances, and it cannot communicate with the "Host"
Using something like a "getter" and "setter" function for the Api to be read and written. I'm unsure on how to implement this, so any help on directions on how to implement this is welcome!
A mixture of 1/2?
There's no good way to prevent having a "third party" widget overwrite the a global variable. Generally it is the responsibility of whoever is putting together the final application to ensure that whatever JavaScripts they are using aren't littering the global namespace and conflicting. The best thing you can do in that direction is give your "Api" a nice, unique name.
What I think can help you a lot is something like the "revealing pattern", which would be a way of doing the "getters and setters" you mentioned, plus more if you needed it.
A simple, useless example would be like the following:
var Api = (function () {
// private variable
var myArray = [];
return {
addItem: function (newItem) {
myArray.push(newItem);
},
printItems: function () {
console.log("lots if items");
}
};
})();
Api.addItem("Hello, world");
Api.extensionValue = 5;
I think you should make a clear delineation of what is shared, or "singleton" data, and keep those items private, as with myArray in my example.
Make it a constant:
const Api = "hi";
Api = 0;
alert(Api); //"hi"
Take a look at
Object.freeze
More info here
Here is a code example from Mozilla's page:
var obj = {
prop: function (){},
foo: "bar"
};
// New properties may be added, existing properties may be changed or removed
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
var o = Object.freeze(obj);
assert(Object.isFrozen(obj) === true);
// Now any changes will fail
obj.foo = "quux"; // silently does nothing
obj.quaxxor = "the friendly duck"; // silently doesn't add the property
// ...and in strict mode such attempts will throw TypeErrors
function fail(){
"use strict";
obj.foo = "sparky"; // throws a TypeError
delete obj.quaxxor; // throws a TypeError
obj.sparky = "arf"; // throws a TypeError
}
fail();
// Attempted changes through Object.defineProperty will also throw
Object.defineProperty(obj, "ohai", { value: 17 }); // throws a TypeError
Object.defineProperty(obj, "foo", { value: "eit" }); // throws a TypeError
However browser support is still partial
EDIT: see Kernel James's answer, it's more relevant to your question (freeze will protect the object, but not protect reassigning it. however const will) same issue with limited browser support though.
The only way (at least that I can think of) to protect your global variable is to prevent the Widgets from having a direct access to it. This can be achieved by using frames functions, as you suggested. You should create an object that contains all the functions that the Widgets should be able to use, and pass such to each Widget. For example:
var Api = {
widgetApi = {
someFunction: function(){
// ...
}
},
addWidget:function(){
var temp = this.widgetApi.constructor();
for(var key in this.widgetApi)
temp[key] = clone(this.widgetApi[key]);
return temp;
}
// Include other variables that Widgets can't use
}
This way, the Widgets could execute functions and communicate with the host or global variable Api. To set variables, the Widget would be editing its private object, rather than the global one. For every frame (that represents a Widget), you must initialize or create a copy of the widgetApi object, and probably store it inside an array, in such a way that an instance of a Widget is stored in the main Api object.
For example, given <iframe id="widget"></iframe>
You would do the following:
var widget = document.getElementById("widget");
widget.contentWindow.Api = Api.addWidget();
widget.contentWindow.parent = null;
widget.contentWindow.top = null;
Additionally, in every frame you would need to set the parent and top variables to null so that the Widgets wouldn't be able to access the data of the main frame. I haven't tested this method in a while, so there might be ways to get around setting those variables to null.