nextProps keys mysteriously inaccessible though it has content - javascript

I'm in componentWillReceiveProps(nextProps) in a react-redux app
console.log("Debug NextProps: ",nextProps.variantCategories);
console.log("Debug NextProps: ",Object.keys(nextProps.variantCategories));
//nextProps.variantCategories[1] returns undefined <-- I need to access this
I got an empty array for Object.keys and so I thought I might be accessing things wrongly and tested the same on a brand new object i,
let i={1: {key: "value"}};
console.log("Debug i: ",i);
console.log("Debug i: ",Object.keys(i));
And here's the console (expanded on the right column)
What am I doing wrong?

What is most likely the case that you are seeing is that the property 1 is not enumerable, that is to say you are not allowed to access it in a traditional sense as a property.
The inspectors in most browsers nowadays reveal all aspects of a JavaScript Object in the console, but that doesn't mean your code will have the same permission or access to retrieve that data. To recreate the issue that appears to be happening, you can try this code snippet:
var specialObject = Object.defineProperty({}, '1', {
enumerable: false,
value: 'some stuff here'
});
// console logging in an inspector would show the full contents
console.log(specialObject);
// prints: > Object {1: "some stuff here"}
// but trying to access the property as an enumerated value will not work:
console.log(Object.keys(specialObject));
// prints: > []
So the question I have - where is that data coming from? Seems like you were expecting a more plain object.

I mutated the object before that the app started to react in such a strange and mysterious way!
And this can be found by checking console logs done before fetching data and those would retain values that are current values.

Related

How to properly log error object in javascript

I am a fairly new to web development and have not given much thought to error. But today I noticed something I have to use json.stringyfy() to see the entire error object. Also message key is not shown in statement 2 but when I print error.message I get a message instead of undefined.
"message" is not even a key(check statement 4) but still logging error.message logs a value(typeof(error.message) is string) .
try {
//Some error occours
} catch (error) {
console.log(JSON.stringify(error)) //statement 1
console.error(error) //statement 2
console.log(error.message) //statement 3
console.log(Object.keys(error)) //statement 4
}
statement 1 logs
MongoServerError: E11000 duplicate key error collection: trendyApp.Markets index: name_1 dup key: { name: "murat market" }
at D:\web projects\trendyApp\server\node_modules\mongodb\lib\operations\insert.js:51:33
at D:\web projects\trendyApp\server\node_modules\mongodb\lib\cmap\connection_pool.js:273:25
at handleOperationResult (D:\web projects\trendyApp\server\node_modules\mongodb\lib\sdam\server.js:363:9)
at MessageStream.messageHandler (D:\web projects\trendyApp\server\node_modules\mongodb\lib\cmap\connection.js:474:9)
at MessageStream.emit (events.js:375:28)
at processIncomingData (D:\web projects\trendyApp\server\node_modules\mongodb\lib\cmap\message_stream.js:108:16)
at MessageStream._write (D:\web projects\trendyApp\server\node_modules\mongodb\lib\cmap\message_stream.js:28:9)
at writeOrBuffer (internal/streams/writable.js:358:12)
at MessageStream.Writable.write (internal/streams/writable.js:303:10)
at TLSSocket.ondata (internal/streams/readable.js:726:22) {
index: 0,
code: 11000,
keyPattern: { name: 1 },
keyValue: { name: 'murat market' }
}
statement 2 logs
{"index":0,"code":11000,"keyPattern":{"name":1},"keyValue":{"name":"murat market"}}
statement 3 logs
E11000 duplicate key error collection: trendyApp.Markets index: name_1 dup key: { name: "murat market" }
I saw this behavior while I was making an express application and the error is generated by mongoose but I think this would be common throughout javascript
statement 4 logs
[ 'index', 'code', 'keyPattern', 'keyValue' ]
"message" is not even a key(check statement 4)
It is, but Object.keys is designed to list enumerable properties, and message is not enumerable.
In the ECMAScript specification, we see in the section on the Error constructor that the constructor creates its message (and other properties) with the procedure:
Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
Other -- custom -- properties can of course be added to Error objects by JavaScript code, which explains why other properties are listed by Object.keys().
As to the output of console.log: the console API implementation has a lot of freedom on how to display objects -- lot's of differences can be found between implementations.
To also output those non-enumerable properties, use
console.log(Object.getOwnPropertyNames(error))
As #trincot said, it is not shown because of message is a non enumerable attribute for Error constructor, the MongoServerError is overriding the MongoError and actually this is overriding the JS Error object, so if you need to know what attributes you have into the error you can check the previous links to see what attributes you can check.
Also if you want to get all the attributes (included the non enumerable ones) you can use Object.getOwnProperties(error) to see all the attributes that the given object has.
Also you can use Object.getOwnPropertyDescriptors(error) to know what is the descriptor for each attribute you've defined into the given object.
Sadly error stack and message fields are not enumerable, below is a utility function that extracts all properties from an error object, even custom fields you assign to errors:
const err = new Error('Something bad happened.');
err.metadata = { custom: { field: '1' } };
console.log(errorToPOJO(err)); // {"stack":"Error: Something bad happened.\\n at <anonymous>:1:13","message":"Something bad happened.","metadata":{"custom":{"field":"1"}}
function errorToPOJO(error) {
const ret = {};
for (const properyName of Object.getOwnPropertyNames(error)) {
ret[properyName] = error[properyName];
}
return ret;
};
Try this if your using api
console.log(error.message.toString())

How can I override the way Proxies are displayed in console?

I've looked through the docs on Proxies, and there is a long list of function properties which can be overridden. Unfortunately, none of them explicitly mention a link to console.log(), and I can't infer which ones console.log() might interact with.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
I've also seen this Stack Overflow question (Javascript Proxy and spread syntax, combined with console.log) which seems to show something similar to what I'm looking for, but more targeted at getting information about a property than the way it's displayed in console.
The issue is, whenever I console.log my proxies, I get output like this:
Proxy { <target>: {…}, <handler>: {…} }
Further, my target is (in my case) totally empty.
const foo = { foo: "foo string" };
const bar = { bar: "bar string" };
let currentObj = foo;
const proxy = new Proxy({}, {
get: (_, prop) => currentObj[prop],
});
console.log(proxy.foo);
console.log(proxy); // would like this to be { foo: "foo" }
currentObj = bar;
console.log(proxy.bar);
console.log(proxy); // would like this to be { bar: "bar" }
So, in order to make this code possible to debug, I need some way to tell console.log what object to output.
Is it possible to override some aspect of a Proxy handler such that console.log() will output arbitrary, use-case specific data?
You could override the toString method and log your object with its toString() method.
Or you could provide a different view-specific log function such as oob.log => () => obj.name

Chrome Devtools console not working in certain websites

I'm doing some studies in Javascript using Twitter website as example. In any website i can open Google Devtools and use the console to manipulate anything on the page, such as get DOM nodes, edit elements and so on.
For instance, when i open 'www.google.com', go to Devtools and type the command above :
console.log('testing');
I get 'testing' string showing in the console.
However, when i open 'www.twitter.com' and do the same thing, NOTHING happens. Nothing is shown in console, just an 'undefined' string as shown below :
Why this behaviour happens only on Twitter website ?
EDIT : Tried the solution proposed on
"delete" - restore native function not working for changed prototype, how then?
But did not work :
In Javascript you can modify your global objects, so it's possible for you to do something like
Array.prototype.push = function(element) {
this[this.length] = 'blah'
}
Now every time you add a element to any array it will always add 'blah';
const myArray = [];
myArray.push(1);
myArray.push(2);
myArray.push(3);
console.log(myArray);
// output ['blah', 'blah', 'blah']
In the twitter website they did the same, although the code is minified you can see it here:
, Y = ["log", "warn", "debug", "info"]
, K = function() {}
, Q = ""
Line 1414 file https://abs.twimg.com/k/en/init.en.caa653749241467e7dbb.js
To make it work again, copy each line and run it on your console (credits for this solution to Rob W):
var frame = document.createElement('iframe');
document.body.appendChild(frame);
console = frame.contentWindow.console
console.log('it works')
If you type in just console.log (without any brackets), you can list the code for the log function on that website. Doing this on Twitter gives you
function (){}
Which is indeed an empty function showing that they've overwritten the default. By contrast, the same on google.com gives
function log() { [native code] }
Which is the default.

Trying to test whether element has attribute without directly assessing the element

Trying to create query object on clicks on various buttons by sending attributes on html called "attr-{{foo}}" which get their name from an ng-repeat or some other means. I wanted on controller that first tests if the element has all attributes that map onto the keys of the query object in the scope, and continue this if else chain down to having only one attribute; however, when I tried this, I would get the error "cannot find .value of 'null'", as I was testing with
(below you will find a snippet that is representative of my controller)
vm.openFoos = function (event) {
if (event.target.attributes.getNamedItem('attr-foo').value &&
!event.target.attributes.getNamedItem('attr-bar').value) {
var obj = {
foo: event.currentTarget.attributes.getNamedItem('attr-foo').value,
name: $routeParams.name
} else if {
(event.target.attributes.getNamedItem('attr-foo').value &&
event.target.attributes.getNamedItem('attr-bar').value) {
var obj = {
foo: event.currentTarget.attributes.getNamedItem('attr-foo').value,
name: $routeParams.name,
bar: event.currentTarget.attributes.getNamedItem('attr-bar').value
}
}
data.getReviews(obj)
.success(function (data){$log.debug(data)}).error(function(e){$log.debug(e)});
};
This works if I click on elements with both attr-foo and attr-bar (as by the convention of this testing from most strict case of having attributes to least, I know if I had written the if statements in reverse I would get the error "cannot find value of null". Sample HTML:
<span class="one" ng-click="vm.openFoos($event) attr-foo="foooo">Click FOOO</span>
<span class="two" ng-click="vm.openFoos($event) attr-foo="fo" attr-bar="bar">Click FOO BAR</span>
Clicking span.one throws the error "cannot find value of null" but clicking span.two works fine.
I do not want to write a controller for each instance of the combinations of keys in my query appearing on various html attributes; however, I'm getting this error.
instead of using
if (event.currentTarget.attributes.getNamedItem('attr-thing').value ....
use
if (event.currentTarget.attributes.getNamedItem('attr-thing') ....
When
event.currentTarget.attributes.getNamedItem('attr-thing')
does not exist, obviously one cannot take the value of it!
Could you be looking for hasOwnProperty?
object.hasOwnProperty("attrname"); // Yields true or false.

Sharing addon objects (content scripts) with Web content (page objects) in Firefox

I spent days trying to share one of my Firefox for Android extension objects with the webpages I also open from my extension (declared as resources). The thing is I have read a lot about the last year's changes about unsafewindow, so I tryed a very small example with the new functions but didn't work. I copied the examples and I also tryed my owns, but there is no way to copy existing objects with functionality. See, I have a VERY big object to clone in the content window, but I decided to test with a small one:
//From addon
var dog = {
name: 'Spike',
woof: function(){alert('woof woof!')}
};
And after that I tryed to copy this object into the active window:
//From addon
var contentWindow = window.BrowserApp.selectedBrowser.contentWindow;
contentWindow.dog = Components.utils.cloneInto(
dog,
contentWindow,
{cloneFunctions: true}
);
And after that, I tryed to check what was really copied:
alert(contentWindow.dog); //Shows: [object Object]
alert(contentWindow.dog.name); //Shows: Spike
alert(contentWindow.dog.woof); //Shows: undefined
So, I can clone the objects but no the functions, even when I declared "cloneFunctions: true".
I also tryed to create an empty object and then assign the functions (a lot of work thinking in my so big original object), and didn't work:
function greetme(user) {
return "cheers " + user;
}
var foo = Components.utils.createObjectIn(contentWindow,{defineAs: "foo"});
Components.utils.exportFunction(greetme, foo, {defineAs: "greetme"});
//foo is not an object in current window
So... Any idea is welcome, I really don't know what to do because theory and given examples doesn't work anymore.
Thanks (A LOT) in advance!!
https://blog.mozilla.org/addons/2014/04/10/changes-to-unsafewindow-for-the-add-on-sdk/
Your code is more or less correct already, however, you're running into trouble with XRay wrappers. And, in order for the content window (website) to actually see you dog, you need to waive the XRay wrapper on the content window as well.
I tested the following with the current Firefox for Android Nightly (sorry, my release Firefox is not configured for remote debugging).
Ran this in the Main Process (using the WebIDE):
var dog = {
name: 'Spike',
woof: function () {
alert(contentWindow.document.title + "\n" + this.name + ': woof woof!');
}
};
var contentWindow = BrowserApp.selectedBrowser.contentWindow;
// Need to unwrap this, so that we can actually set properties on the
// object itself and not just the wrapper. Aka. make "dog" visible to
// the actual script.
var unsafeWindow = Components.utils.waiveXrays(contentWindow);
// Define Window.dog (on the unsafe window, so that the website code
// can actually see it).
unsafeWindow.dog = Components.utils.cloneInto(dog, contentWindow, {
cloneFunctions: true
});
Then I switched over to the actual tab and tested:
dog.woof();
And it worked.

Categories