Javascript null check performance (compare if and try/catch) - javascript

I have to get a value of a inner component. Which is better 1 or 2 when compared ONLY for performance, this method is called several times. The foo may be null depending on the runtime, but foo may have a value but bar may be null etc. It is not an error case or unexpected case that any of foo, bar, innerfoo, innerbar are null, any of them can be null at the runtime depending on the hierarchy. But innerbar can not have any value of if foo is null so there is no need to check the inner components if foo is not defined.
//1
function getInnerValue() {
if (foo && foo.bar && foo.bar.innerfoo && foo.bar.innerfoo && foo.bar.innerfoo.innerbar) {
return foo.bar.innerfoo.innerbar.x;
}
return 0;
}
//2
function getInnerValue() {
try {
return foo.bar.innerfoo.innerbar.x;
}
catch (e) {
return 0;
}
}

Anything with a try catch that ever gets triggered will incur a large performance hit.
Personally I don't find the long chain of truthy checks any less readable than a try catch block. Just longer, ugly and verbose. But really, would anyone ever have trouble reading that (or editing it)? Or better put, would you even need to read it to understand what it is doing?
In fact if anything the try catch block feels like more of a hack. I still have to think about why I'm trying to catch an exception, and it's still ugly.
In any case, we now have optional chaining, so the above doesn't even really matter anymore. I'd go with something like return x?.y?.z?.a?.b?.c?.d||0.
You can of course go with guard functions, as you've probably already noticed reading the thread you've linked. Something like lodash _.get or your own guard(a,'b','c','d').
optional chaining browser compatibility
Babel transpiled output of optional chaining
x?.y will immediately return undefined if x is null or undefined
Browser compatibility is relatively new. Check table of compatibility. You should add Babel transpilation to your pipeline.
optional chaining without transpilation:
foo = {}
function fn() {
if (foo?.bar?.innerfoo?.innerbar)
return foo.bar.innerfoo.innerbar.x
return 0
}
console.log(fn())
foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}
console.log(fn())
transpiled code:
foo = {}
function fn() {
var _foo, _foo$bar, _foo$bar$innerfoo;
if ((_foo = foo) === null || _foo === void 0 ? void 0 : (_foo$bar = _foo.bar) === null || _foo$bar === void 0 ? void 0 : (_foo$bar$innerfoo = _foo$bar.innerfoo) === null || _foo$bar$innerfoo === void 0 ? void 0 : _foo$bar$innerfoo.innerbar) return foo.bar.innerfoo.innerbar.x;
return 0;
}
console.log(fn())
foo = {bar: { innerfoo: { innerbar: { x: 5 }}}}
console.log(fn())

Related

How to tell if a JavaScript operator is supported?

Just realized my script doesn't work on my iphone, and found that the following line is the culprit.
return (this.currentElements[0].labels[0].textContent ?? 'This field') + " must be one of " + validValues.join(', ');
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator indicates that Safari 13.4 supports it, and I think I have 13.3. Regardless, need to resolve.
Is there a way to tell if an operator such as ?? is supported by executing JavaScript on the client? If so, I would detect and execute some alternate approach if necessary.
A little off topic, but does anyone know of a polyfill that will work with Safari and iOS?
You can try this check
const operatorSupported = eval('null??true')
but the eval will throw if ?? is not supported so function should be try...catch
const operatorSupported = (() => {
try {
return eval('null??true');
} catch(err){
return false
}
})();
In order to run it once, the block is wrapped into anonymous function so eval becomes a bit performant
Instead of checking to see if the function works, you can just rewrite it so that the end result will be the same.
_content = this.currentElements[0].labels[0].textContent;
return ((_content === null || _content === undefined) ? 'This field' : _content) + " must be one of " + validValues.join(', ');
You can even have a reusable function that does the same thing:
function nullish(a,b){
return (a === null || a === undefined) ? b : a
}
How to detect operator support?
Using try/catch alone is impossible. You can try/catch expressions but not new syntax. For example this code wouldn't even parse:
const support_dollar_operator = () => {
try {
1 $ 2;
return true;
} catch (e) {
return false;
}
}
support_dollar_operator(); // your browser exploded before that
However it is possible with eval:
const support_dollar_operator = () => {
try {
eval(`1 $ 2`);
return true;
} catch (e) {
return false;
}
}
support_dollar_operator();
//=> false
Is it possible to polyfill an operator?
Yes and no.
Yes you can use a new operator with tools such as Babel but behind the scene it will rewrite your code if that operator isn't supported by your target audience. It would look like this:
// your code
bar ?? 'baz'
// transpiled
bar == undefined ? 'baz' : bar
How do you fix your issue?
You haven't posted an error message so we can't assume that your issue is caused by ?? not being supported.
This would fail even if ?? is supported:
var foo;
foo.bar ?? 'baz';
//=> ERR: Uncaught TypeError: Cannot read property 'bar' of undefined
The ?? operator isn't meant to provide a fallback if something along the path is undefined. This is the job of the ?. "optional chaining" operator:
var foo;
foo?.bar ?? 'baz';
//=> 'baz'
In your code an error could be caused if either A or B is undefined:
(this.currentElements[0].labels[0].textContent ?? 'This field')
// ^ ^
// A B
The good news is that the ?. operator is supported in Safari on iOS

Typescript Function Weird Void || && Behaviour

Why is returnObjectcausing an compilation TypeError returnObject2 isn't?
Normally, void || something should return something while void && something should return void.
But in Typescript, it's the opposite that occurs.
var returnVoid = function(){};
var returnObject = function(){ //Typescript compilation TypeError.
//No best common type exists among return expressions.
if(Math.random() < 0.5)
return new Error();
return returnVoid() || null; //always return null
}
var returnObject2 = function(){ //works
if(Math.random() < 0.5)
return new Error();
return returnVoid() && null; //always return undefined
}
Note: TypeError occurs during the compilation, not in the runtime.
EDIT: I did another test. Shouldn't returnNum2 be () => number too considering (undefined || something) === something? Note: Same behaviour for void 0.
var returnNum = function(){ //() => number
return undefined || 0;
}
var returnVoid = function(){};
var returnNum2 = function(){ //() => void | number
return returnVoid() || 0;
}
It's been pointed out in comments, but I think in general you just need to understand that function(){} will return undefined, which has specified behavior for logical operators.
For undefined && somevalue, undefined will always be returned. For undefined || somevalue, somevalue will be evaluated and returned.
Here's a good reference for more information: http://www.javascriptkit.com/jsref/comparison_operators.shtml
EDIT: The question isn't about what is returned for the logical operation, but why typescript gives error TS2354: No best common type exists among return expressions. on compilation.
This does seem like an error, but may make sense in the context. If you replace the logical operators with just a call to returnVoid() only, you'll get the same error in both functions. Static typing allows the && operator to short-circuit entirely for typing, since something && null will never evaluate to a type, but something || null could depending on what the something is.
Related to this, in typescript you cannot explicitly specify null or undefined as a return type for a function.
While I understand why this may be the case, I agree it is a little odd. It might be worth checking with the folks who make Typescript and filing a bug.
TypeScript doesn't special case expressions of the form undefined || T or void || T to be T because a) you shouldn't write that code (use the comma operator!) and b) it's not safe to write this code because return value contravariance means you're not guaranteed to have a falsy value just because you have a void-returning function reference.
Consider if you wrote code like this:
type callback = (arg: any) => void;
function doSomething(x: callback) {
return x(10) || 'Hello, world!';
}
var x = [];
var add = (arg: any) => x.push(arg);
console.log(doSomething(add)); // Prints '1', not 'Hello, world!'

check if function is a generator

I played with generators in Nodejs v0.11.2 and I'm wondering
how I can check that argument to my function is generator function.
I found this way typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function) but I'm not sure if this is good (and working in future) way.
What is your opinion about this issue?
We talked about this in the TC39 face-to-face meetings and it is deliberate that we don't expose a way to detect whether a function is a generator or not. The reason is that any function can return an iterable object so it does not matter if it is a function or a generator function.
var iterator = Symbol.iterator;
function notAGenerator() {
var count = 0;
return {
[iterator]: function() {
return this;
},
next: function() {
return {value: count++, done: false};
}
}
}
function* aGenerator() {
var count = 0;
while (true) {
yield count++;
}
}
These two behave identical (minus .throw() but that can be added too)
In the latest version of nodejs (I verified with v0.11.12) you can check if the constructor name is equal to GeneratorFunction. I don't know what version this came out in but it works.
function isGenerator(fn) {
return fn.constructor.name === 'GeneratorFunction';
}
this works in node and in firefox:
var GeneratorFunction = (function*(){yield undefined;}).constructor;
function* test() {
yield 1;
yield 2;
}
console.log(test instanceof GeneratorFunction); // true
jsfiddle
But it does not work if you bind a generator, for example:
foo = test.bind(bar);
console.log(foo instanceof GeneratorFunction); // false
I'm using this:
var sampleGenerator = function*() {};
function isGenerator(arg) {
return arg.constructor === sampleGenerator.constructor;
}
exports.isGenerator = isGenerator;
function isGeneratorIterator(arg) {
return arg.constructor === sampleGenerator.prototype.constructor;
}
exports.isGeneratorIterator = isGeneratorIterator;
In node 7 you can instanceof against the constructors to detect both generator functions and async functions:
const GeneratorFunction = function*(){}.constructor;
const AsyncFunction = async function(){}.constructor;
function norm(){}
function*gen(){}
async function as(){}
norm instanceof Function; // true
norm instanceof GeneratorFunction; // false
norm instanceof AsyncFunction; // false
gen instanceof Function; // true
gen instanceof GeneratorFunction; // true
gen instanceof AsyncFunction; // false
as instanceof Function; // true
as instanceof GeneratorFunction; // false
as instanceof AsyncFunction; // true
This works for all circumstances in my tests. A comment above says it doesn't work for named generator function expressions but I'm unable to reproduce:
const genExprName=function*name(){};
genExprName instanceof GeneratorFunction; // true
(function*name2(){}) instanceof GeneratorFunction; // true
The only problem is the .constructor property of instances can be changed. If someone was really determined to cause you problems they could break it:
// Bad people doing bad things
const genProto = function*(){}.constructor.prototype;
Object.defineProperty(genProto,'constructor',{value:Boolean});
// .. sometime later, we have no access to GeneratorFunction
const GeneratorFunction = function*(){}.constructor;
GeneratorFunction; // [Function: Boolean]
function*gen(){}
gen instanceof GeneratorFunction; // false
TJ Holowaychuk's co library has the best function for checking whether something is a generator function. Here is the source code:
function isGeneratorFunction(obj) {
var constructor = obj.constructor;
if (!constructor) return false;
if ('GeneratorFunction' === constructor.name || 'GeneratorFunction' === constructor.displayName) return true;
return isGenerator(constructor.prototype);
}
Reference: https://github.com/tj/co/blob/717b043371ba057cb7a4a2a4e47120d598116ed7/index.js#L221
As #Erik Arvidsson stated, there is no standard-way to check if a function is a generator function. But you can, for sure, just check for the interface, a generator function fulfills:
function* fibonacci(prevPrev, prev) {
while (true) {
let next = prevPrev + prev;
yield next;
prevPrev = prev;
prev = next;
}
}
// fetch get an instance
let fibonacciGenerator = fibonacci(2, 3)
// check the interface
if (typeof fibonacciGenerator[Symbol.iterator] == 'function' &&
typeof fibonacciGenerator['next'] == 'function' &&
typeof fibonacciGenerator['throw'] == 'function') {
// it's safe to assume the function is a generator function or a shim that behaves like a generator function
let nextValue = fibonacciGenerator.next().value; // 5
}
Thats's it.
The old school Object.prototype.toString.call(val) seems to work also. In Node version 11.12.0 it returns [object Generator] but latest Chrome and Firefox return [object GeneratorFunction].
So could be like this:
function isGenerator(val) {
return /\[object Generator|GeneratorFunction\]/.test(Object.prototype.toString.call(val));
}
function isGenerator(target) {
return target[Symbol.toStringTag] === 'GeneratorFunction';
}
or
function isGenerator(target) {
return Object.prototype.toString.call(target) === '[object GeneratorFunction]';
}
Mozilla javascript documentation describes Function.prototype.isGenerator method MDN API. Nodejs does not seem to implement it. However if you are willing to limit your code to defining generators with function* only (no returning iterable objects) you can augment it by adding it yourself with a forward compatibility check:
if (typeof Function.prototype.isGenerator == 'undefined') {
Function.prototype.isGenerator = function() {
return /^function\s*\*/.test(this.toString());
}
}
I checked how koa does it and they use this library: https://github.com/ljharb/is-generator-function.
You can use it like this
const isGeneratorFunction = require('is-generator-function');
if(isGeneratorFunction(f)) {
...
}
By definition, a generator is simply a function that, when called, returns an iterator. So, I think you have only 2 methods that will always work:
1. Accept any function as a generator
2. Actually call the function and check if the result is an iterator
#2 may involve some overhead and if you insist on avoiding that overhead, you're stuck with #1. Fortunately, checking if something is an iterator is pretty simple:
if (object === undefined) || (object === null) {
return false
}
return typeof object[Symbol.iterator] == 'function'
FYI, that still doesn't guarantee that the generator will work OK since it's possible to create an object with the key Symbol.iterator that has a function value that does not, in fact, return that right type of thing (i.e. an object with value and done keys). I suppose you could check if the function has a next() method, but I wouldn't want to call that multiple times to see if all the return values have the correct structure ;-)
A difficulty not addressed on here yet is that if you use the bind method on the generator function, it changes the name its prototype from 'GeneratorFunction' to 'Function'.
There's no neutral Reflect.bind method, but you can get around this by resetting the prototype of the bound operation to that of the original operation.
For example:
const boundOperation = operation.bind(someContext, ...args)
console.log(boundOperation.constructor.name) // Function
Reflect.setPrototypeOf(boundOperation, operation)
console.log(boundOperation.constructor.name) // GeneratorFunction

How do I easily get the value of a nested field in Javascript without null reference exceptions?

I've seen a lot of posts on using the in operator in Javascript to check if a field exists on an object or up the object's prototype chain, but I've seen none for going down the other way.
Let's say I have an object:
var obj = {
myField
}
And myField is set to another object, with various fields on it:
obj.myField = {
mySetting: true
}
If I want to reference mySetting, let's say in an if statement, I have to do something like this:
if (obj.myField && obj.myField.mySetting && obj.myField.mySetting === true)
If I use in, it is still clumsy:
if ("myField" in obj && "mySetting" in obj.myField && obj.myField.mySetting === true)
The following returns false:
if ("mySetting" in obj)
Is there some syntax I'm not aware of that can allow me to write a decent if statement here, returning false if it doesn't exist, or barring that, at least not throw an exception. I use jQuery, so a solution with that would be fine as well.
var a = { b: { c: { d: 10 } } };
var res = [ 'b', 'c', 'd' ].reduce(function(p, c) {
return p ? p[c] : p;
}, a);
You could improve that with some syntactic sugar, I suppose:
function nested_get(obj, path) {
return path.split('.').reduce(function(p,c){return p?p[c]:p;}, obj);
}
var obj = { some: { weird: { nested: 'thing' } } };
alert(nested_get(obj, 'some.weird.nested'));
You are checking for myfield instead of myField that is why you are getting it as false. Remember that JavaScript is case sensitive.
alert(("myField" in obj && "mySetting" in obj.myField && obj.myField.mySetting === true));
Working demo - http://jsfiddle.net/Yfuvu/
I'm not sure there's a nice way to describe a nested property, which you would of course need to do in order to directly check whether it exists or not. As an alternative, you might consider using an intermediate variable to stand in for the field you want to test:
var myField = obj.myField;
if (myField.mySetting && myField.mySetting === true) { /* do stuff */ }
Finally, it's worth at least mentioning the Object.hasOwnProperty method that you might use to check for the existence of a field. Rewriting the example above:
var myField = obj.myField;
if (myField.hasOwnProperty('mySetting') && myField.mySetting === true) {
/* do stuff */
}

What's the simplest approach to check existence of deeply-nested object property in JavaScript? [duplicate]

This question already has answers here:
Test for existence of nested JavaScript object key
(64 answers)
Closed 8 years ago.
I have to check deeply-nested object property such as YAHOO.Foo.Bar.xyz.
The code I'm currently using is
if (YAHOO && YAHOO.Foo && YAHOO.Foo.Bar && YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
}
This works, but looks clumsy.
Is there any better way to check such deeply nested property?
If you expect YAHOO.Foo.Bar to be a valid object, but want to make your code bulletproof just in case it isn't, then it can be cleanest to just put a try catch around it and let one error handler catch any missing segment. Then, you can just use one if condition instead of four that will detect if the terminal property exists and a catch handler to catch things if the intermediate objects don't exist:
try {
if (YAHOO.Foo.Bar.xyz) {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// handle error here
}
or, depending upon how your code works, it might even just be this:
try {
// operate on YAHOO.Foo.Bar.xyz
} catch(e) {
// do whatever you want to do when YAHOO.Foo.Bar.xyz doesn't exist
}
I particularly use these when dealing with foreign input that is supposed to be of a particular format, but invalid input is a possibility that I want to catch and handle myself rather than just letting an exception propagate upwards.
In general, some javascript developers under-use try/catch. I find that I can sometimes replace 5-10 if statements checking input with a single try/catch around a larger function block and make the code a lot simpler and more readable at the same time. Obviously, when this is appropriate depends upon the particular code, but it's definitely worth considering.
FYI, if the usual operation is to not throw an exception with the try/catch, it can be a lot faster than a bunch of if statements too.
If you don't want to use the exception handler, you can create a function to test any arbitrary path for you:
function checkPath(base, path) {
var current = base;
var components = path.split(".");
for (var i = 0; i < components.length; i++) {
if ((typeof current !== "object") || (!current.hasOwnProperty(components[i]))) {
return false;
}
current = current[components[i]];
}
return true;
}
Example usage:
var a = {b: {c: {d: 5}}};
if (checkPath(a, "b.c.d")) {
// a.b.c.d exists and can be safely accessed
}
var _ = {};
var x = ((YAHOO.Foo || _).Bar || _).xyz;
Consider this utility function:
function defined(ref, strNames) {
var name;
var arrNames = strNames.split('.');
while (name = arrNames.shift()) {
if (!ref.hasOwnProperty(name)) return false;
ref = ref[name];
}
return true;
}
Usage:
if (defined(YAHOO, 'Foo.Bar.xyz')) {
// operate on YAHOO.Foo.Bar.xyz
}
Live demo: http://jsfiddle.net/DWefK/5/
If you need to check the correctness of the path, rather than the existance of the "xyz" member on the "YAHOO.Foo.Bar" object, it will probably be easiest to wrap the call in a try catch:
var xyz;
try {
xyz = YAHOO.Foo.Bar.xyz;
} catch (e) {
// fail;
};
Alternately, you can do some string-kong-fu-magicTM:
function checkExists (key, obj) {
obj = obj || window;
key = key.split(".");
if (typeof obj !== "object") {
return false;
}
while (key.length && (obj = obj[key.shift()]) && typeof obj == "object" && obj !== null) ;
return (!key.length && typeof obj !== "undefined");
}
The use as follows:
if (checkExists("YAHOO.Foo.Bar.xyz")) {
// Woo!
};
This problem is solved quite beautifully by coffeescript (which compiles down to javascript):
if YAHOO.Foo?.Bar?.xyz
// operate on YAHOO.Foo.Bar.xyz
use a try catch.
a={
b:{}
};
//a.b.c.d?true:false; Errors and stops the program.
try{
a.b.c.d;
}
catch(e){
console.log(e);//Log the error
console.log(a.b);//This will run
}
I actually voted to close the question as duplicate of javascript convert dotnotation string into objects.
However, I guess it's a different topic, but the answer there might still be helpful if you don't want to try-catch all the time.

Categories

Resources