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
Related
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())
Currently, if I compile a flow/typescript file from something like the following
// #flow
function foo(x: number): string {
if (x) {
return String(x);
}
return "default string";
}
module.exports = foo;
to the following:
function foo(x ) {
if (x) {
return String(x);
}
return "default string";
}
module.exports = foo;
My question is, does there exist a way to transpile the code down to something like following?
function foo(x) {
// preserve typechecking
if (typeof x !== "number")
throw new Error(`Expected x to be of type <number> instead got <${typeof x}>!!!!`);
if (x) {
return String(x);
}
return "default string";
}
Unfortunately, the Microsoft team has decided against the use of preserving type-checking after compiling since that is against their design goals. This was an issue thread on GitHub.
Microsoft has stated in the "non-goals" section, #5, that it will not add/rely on this feature.
However, it is not too difficult to add parameter type checking when it counts.
function printMessage(message: string) {
if (typeof message != "string")
throw new Error("printMessage(message): message must be a string.");
else {
console.log(message);
}
}
I would like to point out that this isn't something javascript was intentionally designed to do. Fighting the grain can sometimes cause more headaches than it is worth.
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!'
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.
So I admit that I'm new to javascript and that I come from a C.+ background ("Hi, I'm Bob, I'm a class-based static language user", chorus "hi Bob!").
I find that I often end up writing functions like:
function someFunc()
{
if (arguments.length === 0 ){
...
} else {
...
}
}
(where there might be three such cases). Or, alternatively, I write the difference into the name:
function someFuncDefault() { ... };
function someFuncRealArg(theArg) { ... };
(Substitute "RealArg" for some semantically contentful phrase).
Is there a better pattern for this kind of thing?
Have a look at this post.
I don't know that I would do it this way, but it seems like it might make your code mildly less unmanageable:
function someFunc() {
switch (arguments.length) {
case 0: noArgs();
case 1: oneArg(arguments[0]);
case 2: twoArgs(arguments[0], arguments[1]);
}
function noArgs() {
// ...
}
function oneArg(a) {
// ...
}
function twoArgs(a, b) {
// ...
}
}
Another example might be:
function someFunc(a, b) {
if ('string' == typeof a) {
// ...
} else if ('number' == typeof a) {
// ...
}
}
And of course you can probably create something quite unmanageable by combining both examples (using conditions to determine behaviour based on number of arguments and types of arguments).
This is overloading, not overriding no?
Javascript is weakly typed, so method signatures and native support is out. My recommendation is to pass an extensible object as the solitary argument. Inspect and handle the existance of properties on the param object as you wish.
What advantage does this have over arguments? Well it lets you be explicit about your intentions where you call, and unambiguous about the meaning of arg1 and arg2 where you recieve, and it lets you abstract to a custom data object class you can extend functionality to.
function someFunc(params)
{
var x = params.x || defaultX;
var y = params.y || defaultY;
//businesslogic
}
someFunc({x:'foo',y:'bar'});
someFunc({y:'baz'});
In Javascript, all arguments are optional.
You might try something like:
Edit (better method that doesn't break for values whose 'truthiness' is false):
function bar(arg1, arg2) {
if(arg1 === undefined) { set default }
if(arg2 === undefined) { set default }
//do stuff here
}
Old method that breaks for falsey values:
function foo(arg1, arg2) {
if(!arg1) { set default }
if(!arg2) { set default }
//do stuff here
}
A great place to start with javascript are Douglas Crockford's javascript lectures: http://video.yahoo.com/search/?p=javascript
Javascript lets you get really lazy with this (not quite as lazy as Python, but pretty lazy).
function someFunc(arg1, arg2)
{
if(typeof(arg1) == "undefined") {
arg1 = default;
}
...
}
So you don't really need to overload. Javascript won't yell at you for passing the wrong number of parameters.
Everyone came close, I think that the real issue here is that in JavaScript you shouldn't change behavior based on parameters being passed in.
Since JavaScript makes all parameters optional you could follow the concise method of using this convention:
function foo(bar, baz) {
bar = bar || defaultBar;
baz = baz || defaultBaz;
.
.
.
}
Personally, I prefer to write the most complex function that would be performed, and then document it in comments so that others know that they do not have to send all the arguments.
//concat(str1, str2 [,str3 [,str4 [,str5]]])
function concat(str1, str2, str3, str4, str5) {
var str = str1 + str2;
if(str3 != undefined)
str += str3;
if(str4 != undefined)
str += str4;
if(str5 != undefined)
str += str5;
return str;
}
I have also found situations where the argument order would matter in a normal function, but sometimes I would want to sent the arguments seperately (i.e. I would want to send str3 and str5 but not str4). For this, I use an object and test the known properties
//concat({str1:string, str2:string, str3:string, str4:string, str5:string})
//str3, str4, and str5 are optional
function concat(strngs) {
var str = strngs.str1 + strngs.str2;
if(strngs.str3 != undefined)
str += strngs.str3;
if(strngs.str4 != undefined)
str += strngs.str4;
if(strngs.str5 != undefined)
str += strngs.str5;
return str;
}
A little more comprehensive overloading mechanism is offered by bob.js:
var notify = new bob.fn.overloadFunction([
{
condition: function(msg) { return bob.utils.isString(msg); },
overload: function(msg) {
console.log(msg);
}
},
{
condition: function(bSayHello) { return bob.utils.isBoolean(bSayHello); },
overload: function(bSayHello, msg) {
msg = bSayHello ? 'Hello: ' + msg : msg;
console.log(msg);
}
}
]);
Calling the overloaded function:
notify('Simple sentence.');
// Output:
// Simple sentence.
notify(true, 'Greeting sentence.');
// Output:
// Hello: Greeting sentence.
notify(123);
// JavaScript Error:
// "No matching overload found."