Identify all non-native variables in the window namespace? [duplicate] - javascript
Is there a way to find out all user defined window properties and variables (global variables) in javascript?
I tried console.log(window) but the list is endless.
You could also compare the window against a clean version of the window instead of trying to snapshot during runtime to compare against. I ran this in console but, you could turn it into a function.
// make sure it doesn't count my own properties
(function () {
var results, currentWindow,
// create an iframe and append to body to load a clean window object
iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
// get the current list of properties on window
currentWindow = Object.getOwnPropertyNames(window);
// filter the list against the properties that exist in the clean window
results = currentWindow.filter(function(prop) {
return !iframe.contentWindow.hasOwnProperty(prop);
});
// log an array of properties that are different
console.log(results);
document.body.removeChild(iframe);
}());
This is in the same spirit as #jungy 's answer but we can do it in 3 lines:
document.body.appendChild(document.createElement('div')).innerHTML='<iframe id="temoin" style="display:none"></iframe>';
for (a in window) if (!(a in window.frames[window.frames.length-1])) console.log(a, window[a])
document.body.removeChild($$('#temoin')[0].parentNode);
First we add a hidden iframe; then we test existing variables against the standard JavaScript API in the iframe; then we remove the iframe.
To work more conveniently, it could be useful to sort the results in alphabetical order, and it's still possible in a 3 lines version:
document.body.appendChild(document.createElement('div')).innerHTML='<iframe id="temoin" style="display:none"></iframe>';
Object.keys(window).filter(a => !(a in window.frames[window.frames.length-1])).sort().forEach((a,i) => console.log(i, a, window[a]));
document.body.removeChild($$('#temoin')[0].parentNode);
And it can be packed into a bookmark:
javascript:document.body.appendChild(document.createElement('div')).innerHTML='<iframe%20id="temoin"%20style="display:none"></iframe>';Object.keys(window).filter(a=>!(a%20in%20window.frames[window.frames.length-1])).sort().forEach((a,i)=>console.log(i,a,window[a]));document.body.removeChild(document.querySelectorAll('#temoin')[0].parentNode);throw 'done';
You would need to do the work for yourself. Read in all properties, on the first possible time you can. From that point on, you can compare the property list with your static one.
var globalProps = [ ];
function readGlobalProps() {
globalProps = Object.getOwnPropertyNames( window );
}
function findNewEntries() {
var currentPropList = Object.getOwnPropertyNames( window );
return currentPropList.filter( findDuplicate );
function findDuplicate( propName ) {
return globalProps.indexOf( propName ) === -1;
}
}
So now, we could go like
// on init
readGlobalProps(); // store current properties on global object
and later
window.foobar = 42;
findNewEntries(); // returns an array of new properties, in this case ['foobar']
Of course, the caveat here is, that you can only "freeze" the global property list at the time where your script is able to call it the earliest time.
I ran this in the console in ChromeDev tool and it copied all of the user defined proper
const getUserDefinedKeys = () => {
const globalKeys = [ 'postMessage','blur','focus','close','parent','opener','top','length','frames','closed','location','self','window','document','name','customElements','history','locationbar','menubar','personalbar','scrollbars','statusbar','toolbar','status','frameElement','navigator','origin','external','screen','innerWidth','innerHeight','scrollX','pageXOffset','scrollY','pageYOffset','visualViewport','screenX','screenY','outerWidth','outerHeight','devicePixelRatio','clientInformation','screenLeft','screenTop','defaultStatus','defaultstatus','styleMedia','onanimationend','onanimationiteration','onanimationstart','onsearch','ontransitionend','onwebkitanimationend','onwebkitanimationiteration','onwebkitanimationstart','onwebkittransitionend','isSecureContext','onabort','onblur','oncancel','oncanplay','oncanplaythrough','onchange','onclick','onclose','oncontextmenu','oncuechange','ondblclick','ondrag','ondragend','ondragenter','ondragleave','ondragover','ondragstart','ondrop','ondurationchange','onemptied','onended','onerror','onfocus','oninput','oninvalid','onkeydown','onkeypress','onkeyup','onload','onloadeddata','onloadedmetadata','onloadstart','onmousedown','onmouseenter','onmouseleave','onmousemove','onmouseout','onmouseover','onmouseup','onmousewheel','onpause','onplay','onplaying','onprogress','onratechange','onreset','onresize','onscroll','onseeked','onseeking','onselect','onstalled','onsubmit','onsuspend','ontimeupdate','ontoggle','onvolumechange','onwaiting','onwheel','onauxclick','ongotpointercapture','onlostpointercapture','onpointerdown','onpointermove','onpointerup','onpointercancel','onpointerover','onpointerout','onpointerenter','onpointerleave','onselectstart','onselectionchange','onafterprint','onbeforeprint','onbeforeunload','onhashchange','onlanguagechange','onmessage','onmessageerror','onoffline','ononline','onpagehide','onpageshow','onpopstate','onrejectionhandled','onstorage','onunhandledrejection','onunload','performance','stop','open','alert','confirm','prompt','print','queueMicrotask','requestAnimationFrame','cancelAnimationFrame','captureEvents','releaseEvents','requestIdleCallback','cancelIdleCallback','getComputedStyle','matchMedia','moveTo','moveBy','resizeTo','resizeBy','scroll','scrollTo','scrollBy','getSelection','find','webkitRequestAnimationFrame','webkitCancelAnimationFrame','fetch','btoa','atob','setTimeout','clearTimeout','setInterval','clearInterval','createImageBitmap','onappinstalled','onbeforeinstallprompt','crypto','indexedDB','webkitStorageInfo','sessionStorage','localStorage','chrome','onformdata','onpointerrawupdate','speechSynthesis','webkitRequestFileSystem','webkitResolveLocalFileSystemURL','openDatabase','applicationCache','caches','ondevicemotion','ondeviceorientation','ondeviceorientationabsolute','WebUIListener','cr','assert','assertNotReached','assertInstanceof','$','getSVGElement','getDeepActiveElement','findAncestorByClass','findAncestor','disableTextSelectAndDrag','isRTL','getRequiredElement','queryRequiredElement','appendParam','createElementWithClassName','ensureTransitionEndEvent','scrollTopForDocument','setScrollTopForDocument','scrollLeftForDocument','setScrollLeftForDocument','HTMLEscape','elide','quoteString','listenOnce','hasKeyModifiers','isTextInputElement' ];
return Object.fromEntries(Object.entries(window).filter(([ key ]) => !globalKeys.includes(key)));
};
const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};
copy(JSON.stringify(getUserDefinedKeys(), getCircularReplacer()));
The properties of window object are in chronological order. So, create some variable with unique name at the beginning of your first script included in your webpage and get the index of this property:
var abcdefghijklmnopqrstuvwxyz = true;
var firstOwnPropertyFound = Object.keys(window).indexOf('abcdefghijklmnopqrstuvwxyz');
Then anywhere you want to get array of all user defined properties use:
let myProp = Object.keys(window).slice(firstOwnPropertyFound);
or if you want to skip the first two variables:
let myProp = Object.keys(window).slice(firstOwnPropertyFound + 2);
The myProp variable is array contains all property names you created. In my test webpage for example:
Array(7) [
0: "abcdefghijklmnopqrstuvwxyz"
1: "firstOwnPropertyFound"
2: "st"
3: "clog"
4: "tstfnc"
5: "tstFNC1"
6: "obj"
length: 7
]
Then access all variables:
myProp.forEach(item => {
console.log(window[item]);
}
I use this and it works. (Sorry for my bad English)
Maybe this?:
for (var property in window)
{
if (window.hasOwnProperty(property))
console.log(property)
}
Related
Define custom array-like object with numeric access
How can I define a collection object in JavaScript that behaves like an array in that it provides access to its items through the numeric index operator? I'd like to allow this code: // Create new object, already done let collection = new MyCollection(); // Add items, already done collection.append("a"); collection.append("b"); // Get number of items, already done console.log(collection.length); // -> 2 // Access first item - how to implement? console.log(collection[0]); // -> "a" My collection class looks like this (only part of the code shown): function MyCollection(array) { // Create new array if none was passed if (!array) array = []; this.array = array; } // Define iterator to support for/of loops over the array MyCollection.prototype[Symbol.iterator] = function () { const array = this.array; let position = -1; let isDone = false; return { next: () => { position++; if (position >= array.length) isDone = true; return { done: isDone, value: array[position] }; } }; }; // Gets the number of items in the array. Object.defineProperty(MyCollection.prototype, "length", { get: function () { return this.array.length; } }); // Adds an item to the end of the array. MyCollection.prototype.append = function (item) { this.array.push(item); }; I think jQuery supports this index access, for example, but I can't find the relevant part in their code.
Your class can extend Array: class MyCollection extends Array {...} - you no longer need the internal array property, the iterator, or the custom length property, and you can define arbitrary methods like append which would simply call this.push(item);. Edit: if you can't use the class keyword, you can do it the old school way: // note - this is no different than doing class MyCollection extends Array {} function MyCollection() { } MyCollection.prototype = new Array(); MyCollection.prototype.constructor = MyCollection; Or you can use JavaScript Proxies so that any time someone tries to read a property you can return the value they're looking for. This shouldn't be "slow" - though it's unavoidably slower than plain objects. Edit: If you really want to make life difficult for yourself and everybody else using your code, you can update your append function to do the following: MyCollection.prototype.append = function (item) { this.array.push(item); this[this.array.length - 1] = item; };
How to check if an object's keys and deep keys are equal, similar to lodash's isEqual?
So I'm in a unique situation where I have two objects, and I need to compare the keys on said objects to make sure they match the default object. Here's an example of what I'm trying to do: const _ = require('lodash'); class DefaultObject { constructor(id) { this.id = id; this.myobj1 = { setting1: true, setting2: false, setting3: 'mydynamicstring' }; this.myobj2 = { perm1: 'ALL', perm2: 'LIMITED', perm3: 'LIMITED', perm4: 'ADMIN' }; } } async verifyDataIntegrity(id, data) { const defaultData = _.merge(new DefaultObject(id)); if (defaultData.hasOwnProperty('myoldsetting')) delete defaultData.myoldsetting; if (!_.isEqual(data, defaultData)) { await myMongoDBCollection.replaceOne({ id }, defaultData); return defaultData; } else { return data; } } async requestData(id) { const data = await myMongoDBCollection.findOne({ id }); if (!data) data = await this.makeNewData(id); else data = await this.verifyDataIntegrity(id, data); return data; } Let me explain. First, I have a default object which is created every time a user first uses the service. Then, that object is modified to their customized settings. For example, they could change 'setting1' to be false while changing 'perm2' to be 'ALL'. Now, an older version of my default object used to have a property called 'myoldsetting'. I don't want newer products to have this setting, so every time a user requests their data I check if their object has the setting 'myoldsetting', and if it does, delete it. Then, to prevent needless updates (because this is called every time a user wants their data), I check if it is equal with the new default object. But this doesn't work, because if the user has changed a setting, it will always return false and force a database update, even though none of the keys have changed. To fix this, I need a method of comparing the keys on an object, rather any the keys and data. That way, if I add a new option to DefaultObject, say, 'perm5' set to 'ADMIN', then it will update the user's object. But, if their object has the same keys (it's up to date), then continue along your day. I need this comparison to be deep, just in case I add a new property in, for example, myobj1. If I only compare the main level keys (id, myobj1, myobj2), it won't know if I added a new key into myobj1 or myobj2. I apologize if this doesn't make sense, it's a very specific situation. Thanks in advance if you're able to help. ~~~~EDIT~~~~ Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null. This answer wasn't very helpful. Here's my working function. function getKeysDeep(arr, obj) { Object.keys(obj).forEach(key => { if (typeof obj[key] === 'object') { arr = getKeysDeep(arr, obj[key]); } }); arr = arr.concat(Object.keys(obj)); return arr; } Usage getKeysDeep([], myobj); Is it possible to use it without having to put an empty array in too?
So, if I understand you correctly you would like to compare the keys of two objects, correct? If that is the case you could try something like this: function hasSameKeys(a, b) { const aKeys = Object.keys(a); const bKeys = Object.keys(b); return aKeys.length === bKeys.length && !(aKeys.some(key => bKeys.indexOf(key) < 0)); } Object.keys(x) will give you all the keys of the objects own properties. indexOf will return a -1 if the value is not in the array that indexOf is being called on. some will return as soon as the any element of the array (aKeys) evaluates to true in the callback. In this case: If any of the keys is not included in the other array (indexOf(key) < 0)
Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null. In the end, this works for me. If anyone can improve it that'd be awesome. function getKeysDeep(obj, arr = []) { Object.keys(obj).forEach(key => { if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key] !== null) { arr = this.getKeysDeep(obj[key], arr); } }); return arr.concat(Object.keys(obj)); } getKeysDeep(myobj);
Get an object just by a property value
Let´s assume I have an object property which is passed into a function. In this case 'name' is filled with 'myObject.name' (which has the value 'Tom') - so basically 'Tom' gets passed into the function as the 'name' function(name) { do something //non-essential for my question } Is it possible to get the object, where 'Tom' is the property of, just by having the information 'Tom'? Basically I´m looking to get myObject. Thanks :)
No, that's not possible. All that the function knows is that one of its parameters was pointed to the string "Tom", not what else points to that string somewhere else in memory.
You can store objects within an array, filter the array to match property name of object to parameter passed to function using for..of loop, Object.entries(), which returns an array of property, values of an object. const data = Array(); const setObjectPropertyName = _name => { data.push({[_name]:_name}); return data } const getObjectByPropertyName = prop => { let res = `${prop} property not found in data`; for (let obj of data) { for (let [key] of Object.entries(obj)) { if(key === prop) return obj; } } return res; } let s = setObjectPropertyName("Tom"); let g = getObjectByPropertyName("Tom"); let not = getObjectByPropertyName("Tome"); console.log(s,"\n", g, "\n", not);
Disclaimer: you absolutely should not do this. I'm only posting this because it is in fact possible (with some caveats), just really not advisable. Going on the assumption that this is running in the browser and it's all running in the global scope (like in a script tag), you could technically iterate over the window object, check any objects in window for a name property and determine if their name property matches the name passed to your function. var myObject = { name: 'Tom', thisIs: 'so awful', imSorry: true, }; function doSomethingWithName(name) { for (var obj in window) { var tmp = window[obj]; if (Object(tmp) === tmp && tmp.name === name) { return tmp; } } } console.log(doSomethingWithName(myObject.name));
JS Object nesting function works, but no idea why
So I was working with a colleague who showed me how i could solve a particular problem: how to get a flat object into a nested object. The object properties are named in such a way that they can be sliced into their relevant key named and then nested. His solution works beautifully, but when I ran through his code myself later I couldn't understand how it works. I'm essentially taking a excel worksheet and creating json from it but for argument sake i'll remove the excel parts and just add the example structure which comes out of the excel parser: //Example data var result = { frame1.title: "heading", frame1.division: "first", frame1.data[0].month: "Jan", frame1.data[0].value: "54", } function deepInsert (o, path, value) { let next, cur = o path.forEach((p,i) => { next = cur[p] if (i == path.length -1) { cur[p] = value } else { if (!next) { next = cur[p] = {} } cur = next } }) } function processWorkbook () { const result = json.map(item => { var o = {foo: "bar"} Object.keys(item).forEach(prop => { deepInsert(o, prop.split('.'), item[prop]) console.log(JSON.stringify(o, 0, ' ')); }) return o }) console.log(JSON.stringify(result, 0, ' ')) } From what I can tell it looks like hes passing in 'o', which is a blank object, then the loop in the deepInsert function is assigning data to not the param, but the object in the calling function, because everytime through the loop, my console log shows more being added to the object. I also don't understand this part: next = cur[p] = {}. For some reason a triple assignment throws me an error in the chrome repl but not in that function? Im just so confused, any help would be great!
The function deepInsert recives the following params: An Object (it will be modified) The array of path for the value( 'foo.bar.x' needs to become ['foo','bar', 'x'] ) The value to be inserted and does this: Iterates on the path Array if the current path iteration isn't the last, it will Initialize a new Object on it. if the current path IS the last one, the passed value is set to it. The function processWorkbook just iterates on the object keys to send the parameters to the deepInsert function. This could be done directly on the deepInsert. And that's it. The problem is the function has unused variables and complicated code. A more simple and documented function: function unnestObject(obj = {}) { let newObject = {}, //The object to return current; // the object position holder for (var key in obj) { // iterate on recived object keys current = newObject // Makes the current object the new Object root let path = key.split('.') // Split the current key path.forEach((p, i) => { // iterate on the key paths if ((path.length - 1) !== i) //if the path isn't the last one current[p] = current[p] || {} // initialize a new object on that path (if a object was previously initialized, it is preserved) else //if the path is the last one current[p] = obj[key] // sets the value of the initial object key current = current[p] // Updates the current to the next node }) } return newObject; //returns the new object } //Example data [DOESNT WORK WITH ARRAYS] var data = { "frame1.title": "heading", "frame1.division": "first", "frame1.data.month": "Jan", "frame1.data.value": "54", } console.log(unnestObject(data)) // Prints // { // "frame1": { // "title": "heading", // "division": "first", // "data": { // "month": "Jan", // "value": "54" // } // } // } Note: Both functions doesn't support arrays, if you pass something like {"foo.bar[0].value": 42}, foo.bar will be a object. You can detect the array [] keys and make it initialize an array instead of an object on the iteration About the next = cur[p] = {}, you can assign one value to multiple variables at once. you can do foo = bar = 42, both will have 42. You can also do foo = bar = {}. both will have pointers to the same object, if you change a value on one, another will already have the change. This is very userful for get and initialize global values for instance var foo = window.foo = window.foo || {bar: 42}; This line will make foo and window.foo recive the object on window.foo . if window.foo wasn't initialized yet, it will recive the new object.
indexOf for array with different objects
I have an array of users. When I click on button "Add New", I want to add new object to this array only if it doen't exist there: var newUser = { 'creating': true, 'editMode': true }; if ($scope.users.indexOf(newUser) < 0) { $scope.users.push(newUser); } but indexOf always return -1. Is this because array contain "different" objects?
I suppose each time you're going to call "Add New", it'll work (hard to say without more code). For the simple reason that each instance of newUser is a different one. The indexOf calls check for exactly this instance of newUser. It doesn't check the property values, just for the reference to the instance. e.g. : var user = {a : "b"}; var users = []; users.push(user); users.indexOf(user); // returns 0, reference the user created at the top, inserted beforehand user = {a : "b"}; users.indexOf(user); // returns -1, reference the user created just above, not yet inserted If you want to check for instance, you'll have to make a check on a property (name, id, ...)
If you want to have a collection of unique values you should use ECMASCRIPT-6 Set If you need to stay legacy, otherwise, you need to use arrays... var Set = (function() { function Set() {} Set.prototype.has = function(val) { return !!this._list.filter(i => i.id === val.id).length; }; Set.prototype.get = function(val) { if(!val) { return this._list; } return this._list.filter(i => i.id === val.id).pop(); }; Set.prototype.add = function(val) { if(!this.has(val)) { this._list.push(val) } return this; } return Set; })();