Strange clause in Javascript the Good Parts - javascript

This code is from the section of recursion.
var getElementsByAttribute = function (att, value) {
var results = [];
walk_the_DOM(document.body, function (node) {
var actual = node.nodeType === 1 && node.getAttribute(att);
if (typeof actual === 'string' &&
(actual === value || typeof value !== 'string')) {
results.push(node);
}
});
return results;
};
I don't understand the point of the clause below:
typeof actual === 'string' && (actual === value || typeof value !== 'string')
How is it different from?
typeof actual === 'string' && actual === value

typeof actual === 'string' && (actual === value || typeof value !== 'string')
This will return true if and only if actual is a string, and either actual === value, or value is not a string.
typeof actual === 'string' && actual === value
This will return true if and only if actual is a string and either actual === value.
In other words, the first condition returns true if value is anything other than a string, whereas the second condition will only return true if it is a string, and strictly equal to actual.

Related

How do I check the type of a variable inside an `if` statement

There is a problem in this area
if (components[i] == **TextOptionType**) {
I'm self-debugging a plug-in for a program called obsidian.
~ObsidianDevLibrary.ts is located at Importing ~type.ts.
There is a problem in referring to TextOptionType as a value.
How can I solve this?
type.ts
export type TextOptionType = {
[propName: string] : any,
key: string,
placeholder?: string,
autoSave?: boolean,
value?: boolean,
onChange?: onChangeType,
}
ObsidianDevLibrary.ts
for (let i = 0; i < components.length; i++) {
if (components[i] == TextOptionType) {
componentsToReturn.push(this.addText(setting, components[i]))
}
}
Maybe comparing TextOptionType with if is wrong grammar, but I don't know the right way.
It may be intended to verify that the data entering the component is formatted
https://github.com/KjellConnelly/obsidian-dev-tools
Define a type-predicate function that checks for known members of TextOptionType, like so:
function isTextOptionType( x: unknown ): x is TextOptionType {
const whatIf = x as TextOptionType;
return (
( typeof whatIf === 'object' && whatIf !== null )
&&
( typeof whatIf.key === 'string' )
&&
( typeof whatIf.placeholder === 'string' || typeof whatIf.placeholder === 'undefined' )
&&
( typeof whatIf.autoSave === 'boolean' || typeof whatIf.autoSave === 'undefined' )
&&
( typeof whatIf.value === 'boolean' || typeof whatIf.value === 'undefined' )
&&
( typeof whatIf.onChange === 'function' || typeof whatIf.onChange === 'undefined' )
);
}
Used like so:
for( const c of components ) {
if( isTextOptionType( c ) ) {
componentsToReturn.push( this.addText( setting, c ) );
}
}

Javascript typeof and IsNaN

In MDN polyfill function for Array.prototype.includes(), I found the following code.
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
In the above code
typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y) this makes me confusing. If the typeof the variable is a number , isNaN will be always false right? so when this whole condition will return true? Can someone explain or did I understand it wrongly?
In JavaScript, the type of NaN is number. I guess this is because it is the result of mathematical operations.
See this question for more information.
This code will return true if the 2 elements are NaN, but false if the both are not numbers.
As you can see, the 1st example returns true is both isNaN() regardless of their type - which makes a equal b. The 2nd checks if both are numbers before using isNaN():
const checkEq1 = (x, y) => isNaN(x) && isNaN(y)
console.log(checkEq1(NaN, NaN)); // true
console.log(checkEq1('a', 'b')); // true - error
const checkEq2 = (x, y) => typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y)
console.log(checkEq2(NaN, NaN)); // true
console.log(checkEq2('a', 'b')); // false
Nope, if typeof x === 'number', x can be NaN, and then isNaN(x) && typeof x === 'number' will both be true.
No idea why that function just doesn't use Number.isNaN(x) && Number.isNaN(y) though, since that will explicitly check if the value is NaN (as opposed to anything that can be NaN when converted to a number, as with isNaN).

Error while accessing javascript array element inspite of having checks

I am doing following in my javascript code
if(
typeof player['stats'] != undefined &&
typeof player['stats']['guild'] != undefined &&
typeof player['stats']['guild']['master'] != undefined &&
typeof player['stats']['guild']['master']['since'] != undefined
)
However I get error:
Cannot read property 'since' of null
I have been stuck with this for a while. Can any javascript gurus help me please?
typeof returns string, so compare against "undefined"
if(
typeof player['stats'] != "undefined" &&
typeof player['stats']['guild'] != "undefined" &&
typeof player['stats']['guild']['master'] != "undefined" &&
player['stats']['guild']['master'] != null &&
typeof player['stats']['guild']['master']['since'] != "undefined"
)
Just check if the value is truthy:
if(
player['stats'] &&
player['stats']['guild'] &&
player['stats']['guild']['master'] &&
player['stats']['guild']['master']['since'] != undefined // only check the last one as it is probably not an object but another value such as 0 (depending on what your data looks like, if you have it as an object then just remove the != undefined check)
)
You could write a fairly simple object getter function which you pass the object and then a dot-delimited key to find a value like so:
function getObj(obj, key) {
return key.split(".").reduce((acc, cur) => {
if (acc !== undefined) {
return acc[cur];
}
return acc;
}, obj);
}
Then, you can grab the value that you want and see if it's undefined or not:
const player = {
stats: {
guild: {
master: {
since: '2004'
}
}
}
};
const since = getObj(player, 'stats.guild.master.since');
if (since) {
// do some code
}
This is a handy utility function you can use on any object and makes your if statement much prettier.
You can also avoid the multiple lookups with a temporary variable:
player = { stats: { guild: { master: null } } }
if ((temp = player.stats) &&
(temp = temp.guild) &&
(temp = temp.master) &&
(temp = temp.since) !== undefined)
console.log(true , temp)
else
console.log(false, temp)
player.stats.guild.master = { since: 'today' }
if ((temp = player.stats) &&
(temp = temp.guild) &&
(temp = temp.master) &&
(temp = temp.since) !== undefined)
console.log(true , temp)
else
console.log(false, temp)

Why wont my closure function work?

The function below adds arguments within 1 paranthesis fine. For example, it computes addTogether(2,3) = 5 and addTogether(2,"3") = undefined.
However, it fails to compute addTogether(2)(3) = 5, instead giving me the error that "addTogether(...) is not a function". The closure function (return function(x)) is supposed to take into the second argument in addTogether(2)(3), and I'm lost on why it dos not work.
function addTogether() {
if (typeof arguments[0] !== "number" || typeof arguments[1] !== "number") {
return undefined;
} //not harmful but not necessary
var sum = 0;
var num = arguments[0];
if (arguments.length === 1) {
//if only 1 argument in the original function...
if (typeof arguments[0] !== "number") {
return undefined;
//returns undefined if arguments[0] isnt a number
}
return function(x) {
if (typeof arguments[0] !== "number") {
return undefined;
//in the closure/2nd function, if first argument isnt a number then no sum will be provided
} else {
sum = num + x; //x = second given argument
return sum;
}
};
}
if (typeof arguments[0] === "number" && typeof arguments[1] === "number") {
for (var x = 0; x < arguments.length; x++) {
if (typeof arguments[x] === "number") {
sum += arguments[x];
//add the argument[0] and [1] if both are number types, not string or array types or any other types
} else {
sum = undefined;
}
}
return sum;
}
// the above "if" statement is rsponsible for achieving addTogether(2,3) = 5;
}
console.log(addTogether(2)(3));
If you want your function to work like addTogether(2)(3), this means that your
addTogether must take an parameter and return a function. addTogether(2) this call will return a new function, and then call the returned function with the second parameter.
In your case when you compare
if (typeof arguments[0] !== "number" || typeof arguments[1] !== "number")
and call the function with one argument, the second typeof arguments[1] !== "number" returns you true, because the second parameter is undefined, so it is not a number and your function returns undefined.
And in your code you can remove some conditions also. Because the above condition will already check them.
function addTogether() {
if (typeof arguments[0] !== "number") {
return undefined;
}
var sum = 0;
var num = arguments[0];
if (arguments.length === 1) {
return function(x) {
if (typeof arguments[0] !== "number") {
return undefined;
} else {
sum = num + x;
return sum;
}
};
}
if (typeof arguments[0] === "number" && typeof arguments[1] === "number") {
for (var x = 0; x < arguments.length; x++) {
if (typeof arguments[x] === "number") {
sum += arguments[x];
} else {
sum = undefined;
}
}
return sum;
}
}
console.log(addTogether(2)(3));
if (typeof arguments[0] !== "number" || typeof arguments[1] !== "number") already causes addTogether(2) to return undefined.
Placing that if statement at the end of the function or turning that || into an && fixes it.

How can I test two strings for equivalence in JavaScript, considering null and empty string the same?

If I compare "a" and "b", that should be false.
If I compare "a" and "a", that should be true.
If I compare "" and null, that should be true.
I could write my own method, but thought there was perhaps a JavaScript shortcut.
Edit: I was thinking something like this:
areDbSame(s1, s2) {
if (s1 === null) s1 = "";
if (s2 === null) s2 = "";
return s1 === s2;
}
Edit2: Settled on this version:
areDbSame(s1, s2) {
return (s1 === null ? "" : s1) === (s2 === null ? "" : s2);
}
Just before you test the equality of your string, you could do a simple one line enforcement, by converting to '' in the case of null. For example (if you also don't care about undefined, false, etc):
// testString becomes the one you are testing
var testString = myString || '';
If you only want to ensure null is blank
var testString = (myString === null) ? '' : myString;
Then you can simply do your string comparisons using testString, and not worry about the null equalities.
IMO this is the cleanest answer because it doesn't convolute the original equality testing of javascript strings. It is the same as saying, let's split the problem up into two parts.
1) When should my string be considered blank, and
2) Now I can just check for regular string equality.
function areEqualStrings(a, b) {
var otherEqualValues = ['', null];
if(typeof a === 'string' && typeof b === 'string') {
return a === b;
} else if(otherEqualValues.indexOf(a) > -1 && otherEqualValues.indexOf(b) > -1) {
return !a === !b;
} else {
return false;
}
}
When coercing JavaScript values, !null is true and !'' is true, so those would result in being equal.
Here's the test (screenshotted from my console):
This function should do it. It type checks first and short circuits otherwise.
function stringCompare(a, b) {
if (((a === null || typeof a === 'string') ||
(b === null || typeof b === 'string')) &&
((a === '' && b === null) ||
(b === '' && a === null) ||
(a === b))) {
return true;
}
return false;
}
No it hasn`t. The two first cases you can do naturally using operator =.
The third case it is impossible because "" is considered a empty string and null has any type. So they never can be true naturally. To do this, you have to write your own method.
Just to be clear. You can use operators = (equal) to do comparison:
== equal to
`x == 8 false
x == 5 true
x == "5" true
=== equal value and equal type
x === 5 true
x === "5" false
Hope it helps

Categories