Javascript - checking for parameters before using them - javascript

I created a popup function and recently changed the format from this:
createPopUp('My Popup',600,200);
to this method:
createPopUp({ title: 'My Popup', width: 600, height: 200 });
I only want to use the values if they exist, so I am currently doing this:
function createPopUp(config){
if (typeof config.title != 'undefined'){
var theTitle = title;
} else {
var theTitle = 'Untitled';
}
}
However this seems very messy so I have been looking for a more efficient way of checking for incoming parameters or setting default values and a nudge in the right direction would be much appreciated.

You can extend a default set of properties like this:
var defaults = {
title: 'Untitled',
width: 100,
height: 100
}
function createPopUp(config)
{
var opts = {}
for (var k in defaults) {
opts[k] = config[k] || defaults[k];
// sometimes falsy values should be allowed to override defaults
// in that case, use this instead:
// opts[k] = typeof config[k] != 'undefined' ? config[k] : defaults[k]
}
// opts contains values from defaults where config doesn't have them
}
jQuery has a handy $.extend() for this purpose, but you didn't tag your post as such.

Generally, the logical or operator is used for this:
var theTitle = config.title || 'Untitled';
Basically, the right-hand operand is an expression that will -like all expressions- resolve to a definite value. It's processedd from left to right, so if config.title is undefined, the string 'Untitled' will be the resulting value.
You can chain logical operators if you need to, too:
var someVar = obj.val || anotherObject.val || yetAnother.foo || 'bar';
Or, if all operands are objects, and you don't know which one exists:
var foo = (bar || foobar || {property: 'default: none of the objects exist').property;
The object references are grouped together, so JS will first try to resolve the variables (left to right) to an existing value, or, in the end create a new object literal with the desired propert. That reference (the result of the grouped logical-ors) is accessed, and the property .property is assigned to foo... I hope this is at least somewhat clear, if not: I'm sorry.
Caution
some properties might not be set at the instance level, or might be set, but assigned a falsy value. In that case, the logical OR is not enough, and you'll have to use either a ternary or a regular if...else:
var foo = (obj.hasOwnProperty('val') ? obj.val : 'default');//<-- uses val property, even if it's undefined, as long as it's set on the instance
//or:
var foo = 'default';
if ('val' in obj)
{//val is set, either on the instance or on its prototype-chain
foo = obj.val
}
//as ternary:
var foo = ('val' in obj ? obj.val : 'default');

var theTitle = 'Untitled';
if('title' in config && config.title != null && config.title.length > 0){
theTitle = config.title;
}

Related

In React/ES6, is there a more concise way to write a ternary that references an attribute?

In my code right now, I have a bunch of code like this: starterValue={ elementToEdit ? elementToEdit.size : null }
I can't do elementToEdit.size || null or something like that because of elementToEdit isn't defined, it obviously can't get its attribute.
Is there a more concise way to write this or should I just deal? Thanks
Assuming elementToEdit is null when it isn't present, you can do
starterValue={ elementToEdit && elementToEdit.size }
If it is undefined or some other falsy value, that value will be passed through instead, but that's probably also fine in your usecase.
You can use the destructuring assignment :
var obj = {
size: 10
};
var {size} = obj;
console.log(size);
obj = null;
var {size} = obj || {}; // Note here we do not use null, but an empty object
console.log(size);
One option would be to add a helper method and reuse it:
const getVal = (obj, key) => obj ? obj[key] : null
and then you could do starterValue = getVal(elementToEdit, 'size');
There are other tricks you can pull with ES2015 proxies but those require you to have an outer object present.
So, if you were able to refactor your code to instead of having elementToEdit as the outer variable, but instead something like const allElements = { elementToEdit }; Then you could use proxies to set the default value of elementToEdit.size to null if it's not present.
If elementToEdit is already named but possibly undefined, you can just do:
(elementToEdit && elementToEdit.size) || null
If it's not named and no idea if it has subsequently been named/defined, you would have to do a typeof check in the same fashion:
(typeof elementToEdit === 'object' && elementToEdit.size) || null
A concise way to do this in JavaScript is using the get function from Lodash which yields undefined in a case the object is null or undefined, instead of throwing an error:
var el1 = { size: 2 };
var el2 = null;
console.log(_.get(el1, 'size'));
console.log(_.get(el2, 'size'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
If you want to have null instead of undefined, you can use a third parameter for default value:
var el1 = { size: 2 };
var el2 = null;
console.log(_.get(el1, 'size', null));
console.log(_.get(el2, 'size', null));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

What is the Javascript equivalent of Python's get method for dictionaries

Python's get method for dictionaries lets me specify what should be returned if a key doesn't exist. For my current case I want a dictionary returned. How do I do this in Javascript?
There is no javascript equivalent of the python dictionary get method. If you would write it yourself, as a function, it would look like this:
function get(object, key, default_value) {
var result = object[key];
return (typeof result !== "undefined") ? result : default_value;
}
Use it like:
var obj = {"a": 1};
get(obj, "a", 2); // -> 1
get(obj, "b", 2); // -> 2
Note that the requested key will also be found in a prototype of obj.
If you really want a method rather than a function (obj.get("a", 2)), you need to extend the prototype of Object. This is generally considered a bad idea though, see Extending Object.prototype JavaScript
With modern javascript you can use the nullish coalescing operator ??
const result = obj[key] ?? default;
This will return the default value if key doesn't exist in obj. It will also return the default in cases like {myKey: undefined} or {myKey: null}, which may or may not be the desired behavior.
JavaScript has no helper feature to do that. You need to test explicitly.
if ("myProperty" in myObject) {
return { another: "object" };
} else {
return myObject.myProperty;
}
You can use a ternary operator to do the same thing with less code.
return ("myProperty" in myObject) ? myObject.myProperty : { another: "object" };
I prefer to use the logical OR like this:
foo.bar || 'default'
If checks is foo.bar is falsy, so it returns 'default' if bar is undefined.
You just need to care, that foo is an object. Otherwise a ReferenceError is thrown.
You could use a proxy for this (really new ):
var handler = {
get: function(target, name){
return name in target?
target[name] :
"Default";
}
};
var dictionary={"hi":true};
var dict = new Proxy(dictionary, handler);
dict.a = 1;
dict.b = undefined;
console.log(dict.a, dict.b,dict.hi); // 1, undefined,true
console.log(dict.new); //"Default"
//the proxied object gets changed:
console.log(dictionary.a, dictionary.b,dictionary.hi); // 1, undefined,true
console.log(dictionary.new); //undefined
A proxy is an object that reflects all changes and requests trough an handler. In this case we can write/access propertys of dictionary normally, but if we access values that do not exist it'll return "Default"
this works for me
let obj = {"a": 1};
let default = 100
obj["a"] || default; // -> 1
obj["b"] || default; // -> 100
But! there are some limitation, if !!obj["a"] === false we always get default value... so it's better to just check if key in obj, to be completely sure.

Setting a property to null

Having the code:
foo = {};
foo.bar = 78;
foo.bar.baz = null;
testing foo.bar.baz against null:
if( foo.bar.baz === null )
console.log("foo.bar.baz is null");
else
console.log("foo.bar.baz is NOT null");
results in "foo.bar.baz is NOT null". Why isn't it null since i set it up explicitly?
Because primitives don't have properties, and the value in foo.bar is a primitive.
When you access (get or set) a property on a primitive, the JavaScript engine creates an object for that primitive, sets or retrieves the property value (if any), and then throws away the object. (That's why (42).toString() works; the primitive is promoted to an object backed by Number.prototype, toString is retrieved from that object and called with this referring to the object, and then the object is thrown away.)
So although an object was created in your foo.bar.baz = null statement, and its baz property was set to null, that object was never stored anywhere (certainly not in foo.bar), and so it got thrown away. Later when you do if (foo.bar.baz === null), a new object, which doesn't have the property, is created and you get undefined for its baz property (because it doesn't have one). (Naturally, JavaScript engines can optimize this process to avoid unnecessary object creation.)
We could create a function on Number.prototype that returned the object that gets created, to demonstrate that the object creation really does happen each time you access a property on a primitive:
// Add a `nifty` method to numbers
Object.defineProperty(
Number.prototype,
"nifty",
{
value: function() {
console.log("Number#nifty called");
return this;
}
}
);
var n = 42; // A primitive number
console.log(typeof n); // "number"
var obj1 = n.nifty(); // Creates an object, which we keep
console.log(typeof obj1); // "object"
var obj2 = n.nifty(); // Do it again, and a new object is created
console.log(obj1 === obj2); // false, they're not the same object
If you want to set properties on a number and keep them, you can do that by explicitly creating a Number object:
var n = new Number(42);
n.baz = null;
console.log(n.baz === null); // true
It's rare to want to do that, but it's possible. Just beware that as we showed earlier, two Number objects with the same raw value are not == to each other:
var n1 = new Number(42);
var n2 = new Number(42);
console.log(n1 == n2); // false
console.log(n1 === n2); // false
>, <, >=, and <= will coerce the number back to a primitive and use the raw value, but == and === will not.
Working with numeric value as object in strict mode will run into exception.
By default foo.bar.baz = null will be undefined because foo.bar is not object and .baz will not be set.
This is Your code in strict mode:
'use strict';
var foo = {};
foo.bar = 78;
foo.bar.baz = null;
if(foo.bar.baz === null )
alert("foo.bar.baz is null");
else
alert("foo.bar.baz is NOT null");
here is solution:
'use strict';
var foo = {};
foo.bar = {};
foo.bar.num = 78; // your number here
foo.bar.baz = null;
if( foo.bar.baz === null )
alert("foo.bar.baz is null");
else
alert("foo.bar.baz is NOT null");
p.s. always put 'use strict' inside Your JS files to be able to make JS less versatile.
This is because
foo = {};
foo.bar = 78;
foo.bar.baz = null;
console.log(foo.bar.baz); // undefined
since you are trying to set a property of a number.
foo.bar is a number primitive, not an object. You can't set properties on a number primitive, but you can set properties on a number object.
foo = {};
foo.bar = new Number(78);
foo.bar.baz = null;
Now foo.bar.baz == null.

Override the Equivalence Comparison in Javascript

Is it possible to override the equivalence comparison in Javascript?
The closest I have gotten to a solution is by defining the valueOf function and invoking valueOf with a plus in front of the object.
This works.
equal(+x == +y, true);
But this fails.
equal(x == y, true, "why does this fail.");
Here are my test cases.
var Obj = function (val) {
this.value = val;
};
Obj.prototype.toString = function () {
return this.value;
};
Obj.prototype.valueOf = function () {
return this.value;
};
var x = new Obj(42);
var y = new Obj(42);
var z = new Obj(10);
test("Comparing custom objects", function () {
equal(x >= y, true);
equal(x <= y, true);
equal(x >= z, true);
equal(y >= z, true);
equal(x.toString(), y.toString());
equal(+x == +y, true);
equal(x == y, true, "why does this fails.");
});
Demo here: http://jsfiddle.net/tWyHg/5/
That is because the == operator doesn't compare only primitives, therefore doesn't call the valueOf() function. Other operators you used do work with primitives only. I'm afraid you cannot achieve such thing in Javascript. See http://www.2ality.com/2011/12/fake-operator-overloading.html for some more details.
Piggybacking on #Corkscreewe:
This is because you are dealing with Objects and the equivalency operators are only going to compare whether two variables reference the same Object, not whether the two Objects are somehow equal.
One solution is to use "+" in front of the variables and define a valueOf method for the Objects. This calls the valueOf method on each object to "cast" its value to a Number. You have already found this, but understandably do not seem very satisfied with it.
A more expressive solution might be to define an equals function for your Objects. Using your examples above:
Obj.prototype.equals = function (o) {
return this.valueOf() === o.valueOf();
};
var x = new Obj(42);
var y = new Obj(42);
var z = new Obj(10);
x.equals(y); // true
x.equals(z); // false
I know this doesn't do exactly what you want (redefine the equivalency operators themselves), but hopefully it will get you a little closer.
If it's full object comparison you're looking for then you might want to use something similar to this.
/*
Object.equals
Desc: Compares an object's properties with another's, return true if the objects
are identical.
params:
obj = Object for comparison
*/
Object.prototype.equals = function(obj)
{
/*Make sure the object is of the same type as this*/
if(typeof obj != typeof this)
return false;
/*Iterate through the properties of this object looking for a discrepancy between this and obj*/
for(var property in this)
{
/*Return false if obj doesn't have the property or if its value doesn't match this' value*/
if(typeof obj[property] == "undefined")
return false;
if(obj[property] != this[property])
return false;
}
/*Object's properties are equivalent */
return true;
}
you can use the ES6 Object.is() function to check the property of object.
Object.prototype.equals = function(obj)
{
if(typeof obj != "Object")
return false;
for(var property in this)
{
if(!Object.is(obj[property], this[property]))
return false;
}
return true;
}
Adding (); may help depending on your requirement.
var Obj = function (val) {
this.value = val;
}();

How can I avoid using `eval()` in this example?

I'm trying to use a string as a reference to a variable to pass into a function. For example:
var names = ['Peter', 'John'],
var hasName = function(name){
var params = ['names'];
return $.inArray(name, eval( params[0] )) === -1;
};
How to avoid eval()?
EDIT:
The string from params[0] is comming from a data-qval of an input in my html. The array that contains the actual data can be declared anywhere, params[0] is just a reference to that array passed in as string in data-qval, it's a parameter. I pasted my plugin's code here .
http://pastebin.mozilla.org/1598528 Line 101.
Full example: http://jsfiddle.net/elclanrs/ZsS2D/29/
It currently works, I'm just looking for a way get rid of eval()...
In that particular case, just use names:
var names = ['Peter', 'John'],
var hasName = function(name){
var params = ['names'];
return $.inArray(name, names ) === -1;
};
(See also the note below.) (Your edit makes the above not applicable.)
If you're trying to look up the names array in some container using the string "names", you'd have to have a reference to the container, e.g.:
var obj = {
names: ['Peter', 'John'
};
var hasName = function(name){
var params = ['names'];
return $.inArray(name, obj[params[0]] ) === -1;
};
If there is no container other than the variable scope in which you're doing this, you'll have to use eval. But you can (and usually should) adjust things so you have a container (as above) so you can avoid it. Note that if names is declared at global scope, you do have a container (window).
So to summarize:
If names is a var at global scope (or an implicit global), window[params[0]] will give you a reference to it.
If names is already in some container object, you can use container[params[0]] to get a reference to it.
If names is a var within a function, you cannot get at it using a runtime string without eval; ideally, rather than var names = [...];, use var container = {names: [...]}; and then you can use container[params[0]].
Note that your function is called hasName, but it returns true when the array doesn't have the name and false when it does. You probably want !== -1, not === -1.
Isn't this enough?
var hasName = function(name){
return $.inArray(name, names) > -1;
};
Also, notice the comparison
Because JavaScript treats 0 as loosely equal to false (i.e. 0 ==
false, but 0 !== false), if we're checking for the presence of value
within array, we need to check if it's not equal to (or greater than)
-1.
Strings as references to variables? If it's a global object in a browser, it will be in the window object, so you can just do window[variableName] to get its value. Same for objects, i.e. instead of object.foo, you can do object['foo'] or bar = 'foo', object[bar]. For locally scoped variables, you cannot do it without using an object or eval.
if names is global, you can use the global namespace, i.e. window
var hasName = function(name,namespace){
namespace = namespace || window;
return $.inArray(name, namespace.names) > -1;
};
hasName('Peter'); //=> true;
This may also be an idea:
var MYNS = { names:['Peter','John']
,hasName: function(name){
return $.inArray(name, this.names) > -1;
}
};
MYNS.hasName('Peter'); //=>true

Categories