An exception occurred several times inside the following function, running in the (Chrome) browsers of our users. NB the code is minified, I've just added newlines for readabilily.
function a(c, b, e, f) {
for (var d in b) {
if (c && _.isObject(b[d]) === true && _.isArray(b[d]) === false) {
a(c[d], b[d], e, d + ".")
} else {
if (!c || _.isEqual(c[d], b[d]) === false) {
e.push({
name: f + d,
value: b[d]
})
}
}
}
return e
}
The exception was: TypeError: Cannot read property 'beginning' of null
So far my analysis is this:
Known fact: beginning is one of the values of the d variable (a
property of b).
So, the exception occurred when evaluating b[d]or c[d]
Inside the for loop, c can be null, but not b (b null => no loop)
So, the only way to trigger that exception is to have a null c
If c is null, we have to take the else path
If c is null, !c is truthy, so _.isEqual(...) should not be evaluated (see Is that js expression safe: if( !x || doSomething( x[prop], y[prop] ) === false )).
At that point, I've reached a dead end in my reasoning. The exception should not happen. I probably made a mistake somewhere along the line, but where ?
NB: the problem seems to be fixed (I can't reproduce it, so I can't be sure), just by changing the code a bit, adding a separate 'is c null ?' test before the if...else, but that's not very satisfying.
Related
I have a situation in my code where I have several conditions for 3 variables, let's say a, b and c.
I've implemented else if conditions for each case when one of three is false and for when all three are true and for when all three are false. But the problem is that I need to implement conditions also for when 2 out of 3 are false and following the order of a, b and c I need to show specific message for the first in order that is false.
For example, a becomes false, then b becomes false and c remains true, I need to show a specific message for a false. Other case: when b becomes false then a becomes false and c remains true, I need to show a specific message for a false, when c becomes false and b becomes false, I need to show a specific message for b.
How can this be achieved?
Code example
if (a && b && c) {
setOrder(format(order));
} else if (!a && b && c) {
setOrder("orderAlternateNumber");
} else if (!b && a && c) {
setOrder("orderNewNumber");
} else if (!c && a && b) {
setOrder("someOtherNumber");
} else if (!c && !a && !b) {
setOrder("");
}
You can combine these variables into a single number with ones and zeroes and use a simple dictionary lookup for the order parameter:
let n = 1000 + (a * 100) + (b * 10) + (c * 1)
order = {
1111: format(order), // true,true,true,
1011: "orderAlternateNumber", // false,true,true
1101: "orderNewNumber", // true,false,true etc...
1110: "someOtherNumber",
1000: '',
}
setOrder(order[n])
I am not entirely sure what your scenario is, you example is somewhat vague because you don't give a lot information about your actual issue. However based on:
For example, a becomes false, then b becomes false and c remains true, I need to show a specific message for a false.
I would suggest the following. Instead of checking all combinations a, b and c. You can also check them separately.
let error;
if (!a) error ??= "something wrong with a";
if (!b) error ??= "something wrong with b";
if (!c) error ??= "something wrong with c";
if (error) {
// do something with error message
} else {
// handle success
}
The above stores a single error message depending on the first error it encounters. ??= only assigns a value if the current value if nullish. But you could also collect them by storing them in an array.
const errors = [];
if (!a) errors.push("something wrong with a");
// ...
if (errors.length != 0) {
This might not be what your looking for, it's hard to say what is since you withhold your actual scenario.
On the function that changes a, b and c, you need to have a condition.
If one of them is going to be set to false, you have to check if another one, and only one is already false. If there is another false, you can set the message according to this one and commit the change for the second parameter to be false.
If im not clear, you might want to add code example for the function that controls a, b and c for my better understanding.
I´m trying to interpret source codes from big platforms like Facebook to learn how great programmers do their work, some of them have a type of programming that I have been never seen before.
This loop is inside that type of programming:
for (var e; b.length && (e = b.shift()); ) b.length || void 0 === c ? d = d[e] ? d[e] : d[e] = {
}
This is the first time I see something like this, I finally thought that this slice of code was only created for distracting and scare possible malicious users
The loop starts with an undefined variable called 'e'
for (var e;...
And it has no condition to running it, it do nothing, no sense.
b.length && (e = b.shift());
This code means: number of elements inside b and e equals b without the first element
the third loop statement is also empty, there is no increment.
Now the hardcore part, there is no action for every recursion, not even Braces {}
b.length || void 0 === c ? d = d[e] ? d[e] : d[e] = {
}
This code means: number of elements inside b or if undefined equals the content inside the variable c and if d equals the element number e inside d, then do nothing and if not then element number e inside d equals an empty braces {}
As you can see, this code has no sense, it has no reason to exist, but it does, so, Can an expert tell me if this is actually works for something or its just ascii symbols with no utility?
Why does this fail?
The way I read this code is "if either a or b or c equals three, then the statement is true". But apparently JavaScript disagrees. Why?
function test() {
var a = 'one';
var b = 'two';
var c = 'three';
return ( ( a || b || c ) === 'three' );
}
EDIT: am aware of the fact that i need to evaluate each expression separately, but was looking for a quicker way to write it. any suggestions will be welcome.
Your reading of the code is incorrect. Translated to a different form:
if (a) {
return a === "three";
}
if (b) {
return b === "three";
}
if (c) {
return c === "three";
}
The subexpression (a || b || c) returns the first of a, b, or c that is not "falsy". That's a, because its value is "one", so that's the overall value that's compared to "three".
The expression ( a || b || c ) returns anything that is truthy on the first-come-first served basis.
Here a is truthy and hence used. If a is falsey b will be used. If it is falsey too, c will be used.
So, you always end up comparing "one" == "three" since strings are considered truthy. You can make use of Array.some in this case, which does what you want or how you want it to behave, in your words
"if either a or b or c equals three, then the statement is true"
return [a,b,c].some(function(str){
return str == "three";
});
This evaluates to is a, b, or c (which will be true or false) the string-equivalent of "three". Which will always be false.
In order to achieve what you want, you need
return (a === 'three') || (b === 'three') || (c === 'three');
I'm getting the error
TypeError: Cannot call method 'call' of null
in Chrome. After opening the console, setting to catch uncaught exceptions, and reloading the page, it stops on the following line (minified->prettified code):
return this instanceof Q && (b = null != (d = this.getO()) ? d.id : void 0, 0 <= L.call(null != (f = null != c ? "function" === typeof c.get ? c.get("foo") : void 0 : void 0) ? f : [], b))
The only instance of call in that line is at L.call(...). Looking at Scope Variables, I see no instance of L in Local. In Closure, I see:
L: function indexOf() { [native code] }
arguments: null
caller: null
length: 1
name: "indexOf"
__proto__: function Empty() {}
As expected, it's defined and is a function. Inspection of __proto__ reveals a call method.
Further, the console reflects the state shown in Scope Variables:
> L
function indexOf() { [native code] }
> L.call
function call() { [native code] }
> L.call([], b) // This is what it is getting called with
-1
And copying the offending line (minus the return) runs as expected.
> this instanceof Q && (b = null != (d = this.getO()) ? d.id : void 0, 0 <= L.call(null != (f = null != c ? "function" === typeof c.get ? c.get("foo") : void 0 : void 0) ? f : [], b))
false
I can only reproduce this behavior in our production environment. It happens fairly sporadically, with no pattern I've been able to find. We've neither observed nor gotten a report of it happening in any browser other than Chrome. I'm running Chrome 25.0.1364.172, and have logs of this error in Chrome 25.0.1364.152.
This happens partway through a long chunk of javascript (there's a visible pause of a second or so in the animations while it runs). Running it on reduced data sets does not reproduce it.
Does anyone have any debugging tips and/or information on reasons the console could be out of sync with the script in this way?
Edit: I searched the minified source for the variable L, but there are only two places it is set to anything other than [].indexOf, and they are both definitely out of scope when I hit this line.
The same problem occurs in Chrome Canary 27.0.1440.0, but on a different line. The situation is otherwise the same: the variable L is defined but L.call(...) throws a TypeError.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
javascript test for existence of nested object key
I'm attempting to construct an error message for a formset by testing if a certain object is not undefined, and if it's not undefined, then I end up populating it with that error message. The main problem is that I have to validate if each nested object is undefined, which results in some pretty ugly code. Here's the example:
errorsForField: function(fieldName, formsetName, formNumber) {
if (typeof this.model.errors != 'undefined'){
var fieldError = document.createElement('span');
$(fieldError).addClass('field-error');
// THE FOLLOWING LINE THROWS ERROR.
if (formsetName && _.isUndefined(this.model.errors[formsetName][fieldName]) != true) {
$(fieldError).text(this.model.errors[formsetname][fieldName]);
} else if (typeof this.model.errors[fieldName] != "undefined"){
$(fieldError).text(this.model.errors[fieldName]);
}
this.errors[fieldName] = fieldError.outerHTML;
return fieldError.outerHTML;
}
return false;
},
I get an error stating that I cannot determine [fieldName] of an undefined object this.model.errors[formsetName]. In other words, I have to first determine if this.model.errors[formsetName] is empty and then test if [fieldname] is undefined.
This seems like a really cumbersome solution. Any suggestions for changing this?
You can create a library function that takes property names as parameters and returns the final value if it exists, or null:
function TryGetPropertyValue(o, propertyName1 /*, ... propertyNameN */) {
var names = [].slice.call(arguments, 1);
while (o && names.length) {
o = o[names.shift()];
}
return names.length ? null : o;
}
Call it like:
var err = TryGetPropertyValue(this.model.errors, formsetName, fieldName) ||
TryGetPropertyValue(this.model.errors, fieldName);
if (err != null) {
$(fieldError).text(err);
}
If you want it to return undefined instead of null if the field is not found, you can change the function slightly:
function TryGetPropertyValue(o, propertyName1 /*, ... propertyNameN */) {
var names = [].slice.call(arguments, 1);
while (o && names.length) {
o = o[names.shift()];
}
if (names.length == 0) {
return o;
}
}
http://jsfiddle.net/HbggQ/
As Paul suggested, this is an inherent limitation of Javascript. Even Coffeescript (which is just a layer of syntactic sugar on top of JS) doesn't really solve the problem; it just hides the workaround under it's syntactic sugar (which admittedly is really handy)
If you want to stick to Javascript, you basically have two options: use ternary operators, or use boolean operators. Here's examples of each that check A.B.C.D (where A, B, C or D might not exist):
// Returns A.B.C.D, if it exists; otherwise returns false (via ternary)
return !A ? false :
!A.B ? false :
!A.B.C ? false :
A.B.C.D ? A.B.C.D : false;
// Returns A.B.C.D, if it exists; otherwise returns false (via booleans)
return A && A.B && A.B.C && A.B.C.D;
Obviously the latter is a lot shorter. Both solutions rely on Javascript's "truthiness" (ie. that the values 0, "", null, and undefined count as false). This should be fine for your case, as none of those values will have an errors property. However, if you did need to distinguish between (say) 0 and undefined, you could use the ternary style, and replace !A with typeof(A) == 'undefined'.