I have a javascript function that takes two parameters, 'key' and 'value'.
If only one parameter is given then the 'key' parameter is given a default value and the undefined 'value' parameter gets the 'key' parameter value.
function thingSet(key,value){
if(typeof value === 'undefined'){
value=key;
key='_default';
}
//... use key and value
}
The code works but I feel abit uneasy for some reason.
Are there better ways to do this?
You can refactor it like this:
function thingSet (key, value) {
key = key || '_default';
value = value || key;
//... use key and value
}
That's nice short-circuit evaluation allowing you to set default values easily there.
This is pretty standard and heavily used "overloading" mechanism in javascript. You'll find it all over the libraries like jQuery.
Like many dynamic language constructs, there is a gap between what compiler can check for you and what you have to keep as a convention, perhaps documenting it thouroughly.
The power comes at a price of robustness. If you use this kind of tricks, you have to make sure everybody understands the implied API and uses it accordingly.
You can set default values like this:
function thingSet(key,value){
key = key || '_default';
value = value || key;
//... use key and value
}
At least, that is what I make of your function. The unease may be due to the fact that in your function key may be undefined too, in which case the assignment after checking the condition if(typeof value === 'undefined') still may result in an undefined value
You can check for existence of at least one parameter using arguments.length.
function thingSet(key,value){
if (!arguments.length) {
alert('please supply at least one parameter');
return true;
}
key = key || '_default';
value = value || key;
//... use key and value
}
Seems fine to me. The only thing I'd have done differently would be a more direct comparison on value:
if(value == undefined){
I normally do this with JSON
myfunction = function(args){
args.key = (typof(args.key) == "undefined")?args.key = "_default":args.key;
args.value = (typof(args.value) == "undefined")?args.key:args.value;
}
myfunction({key:"something",value:"something else"})
that way you know which variable you are passing to the function and don't have to assume anything from within the function.
It's hard to discuss design questions on a dummy example, but I'd prefer a function that always accepts one parameter, which can be a simple value, or a hash of values. Consider this slightly more realistic example:
function setName(opt) {
if (typeof opt != "object") {
var p = opt.split(" ");
opt = { first: p[0], last: p[1] };
}
$.extend(this, opt);
}
This can be used as person.setName('John Doe') or person.setName({last:'Doe'})
Related
Is it possible to write this in shorter and cleaner way?
I'm reading it from an XML, sometimes the URL value does not exist.
if (typeof(entry[i].getElementsByTagName("url")[0].childNodes[0]) !== "undefined") {
var foo = 'baar'
} else {
var foo = entry[i].getElementsByTagName("url")[0].childNodes[0]
}
It's been years it doesn't make sense anymore to use this construct (unless you don't know whether the variable, not the value, is undefined). undefined is now read only.
Simply use
if (entry[i].getElementsByTagName("url")[0].childNodes[0] === undefined) {
In almost all cases, typeof x === "undefined" is a bad practice.
In the specific case of a DOM element, you can also simply use
if (!entry[i].getElementsByTagName("url")[0].childNodes[0]) {
because you can't have a falsy node, and of course, when the goal is to apply a default value, just use
var foo = entry[i].getElementsByTagName("url")[0].childNodes[0] || 'baar';
(be careful that this test only works when all the parts before the the last [0] are present, it's usually convenient to use querySelector or a DOM selection API like jQuery to make everything less verbose).
var foo = entry[i].getElementsByTagName("url")[0].childNodes[0] || 'baar'
You can write in this way.
var ele = entry[i].getElementsByTagName("url");
if (ele && ele[0].childNodes[0]) {
var foo = 'baar'
} else {
//code
}
There is no need to check it explicitly for undefined.undefined is evaluated as false.
When accessing nested objects using dot notation, I always have to make sure that the previous object exists, which gets pretty exhausting.
I basically want to avoid long if chains like
if (a && a.b && a.b.c && a.b.c[0] ... ) { v = a.b.c[0]; }
The only other thing I can think of is via the use of a try catch.
var v; try { v = a.b.c[0].d.e; } catch (e) {}
Is there a better pattern for this?
I think you've got the two prettiest solutions already.
But note that for something like, say, obj.obj.string.length your first solution will fail if string === "". Since an empty string is falsey, it'll trip the && guard.
But speaking of strings, you could do something like:
function getNestedProperty(obj, propChain) {
var props = propChain.slice(0), prop = props.shift();
if(typeof obj[prop] !== "undefined") {
if(props.length) {
return getNestedProperty(obj[prop], props);
} else {
return obj[prop];
}
}
}
var v = getNestedProperty(a, ["b", "c", 0, "d", "e"]);
Yeah... not too pretty :P
I'd say that, of the solutions proposed, try...catch is probably the simplest way to go
How about this one:
var hasProperty = function (object, property) {
var properties = property.split('.'),
temp = object;
while (temp && properties.length) {
temp = temp[properties.shift()];
}
return !!temp;
};
and then use it like:
if (a && hasProperty(a, 'b.c.0' ) { v = a.b.c[0]; }
The scenario you are referring to in your question is also called "optional chaining". Some languages already support it by now – for example C# has so called null-conditional operators which allow you to short-circuit your expressions:
var count = customers?[0]?.Orders?.Count();
Unfortunately, this feature has not yet made it into the current JS specifications.
There is an open Stage 1 proposol for "optional chaining" that can be tracked here.
This would allow you to write...
a?.b[3].c?.(x).d
...instead of:
a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
If you want to take the risk and use it already at this early stage, you can target it via babel to include it in your project.
It's rather evil, but this should work and doesn't look too horrible:
var i = !a ? null : !a.b ? null : !a.b.c ? null : !a.b.c.d ? a.b.c.d.e;
The reason for the ! is to invert the test flag, to allow the success case to be the last expression in the ?:. That allows us to chain them together like this.
Do check the operator precedence if you want to do this for real (I did some very basic tests and I think I got it right). And do expect people to point and laugh if they see it in your code.
I have the following code...
function foo(type) {
if(foo.type == undefined) foo.type = 0;
}
What I'd like it to do is create a new property on the foo object for each "type" that comes through and set that property to zero (and later begin counting each time a "type" comes through). However, I hit a mental blip, in this case foo.type is probably evaluated as the property type, on foo and not whatever the variable type refers to.
I need to convert the value of type to a property, any ideas?
And yes I know this question's name sucks, if you think of something better just edit the quesiton.
function foo(type) {
if(foo[type] == undefined) foo[type] = 0;
}
You probably want to use === undefined so you won't get any unexpected results
if (typeof foo[type] === 'undefined') {
foo[type] = 0;
}
I'm wondering how to go about doing this. Not sure what the terminology is so I apologize for that. I've seen this behavior in jQuery when you use the .css() method. As you may know already, this method accepts a couple of options:
You can do the following:
$("#box").css("background-color", "red");//sets the bg color to red
$("#box").css("background-color");//returns the bg color of #box
var properties = {"background-color" : "red", "width" : 100};
$("#box").css(properties); //sets multiple properties in one call with literal object.
So, I'm not so much worried about the getter portion of this functionality. I'm most interested in it's ability to differentiate between a variable and a literal object. I'd like to create a plugin that has the same behavior based on the argument it receives. A simple example would be something like this:
function openWindow(URL_OR_OBJECT){
if(variable){
window.open(URL_OR_OBJECT);
return;
}
var opt = URL_OR_OBJECT;
window.open(opt.url, opt.title, opt.options, opt.replace);
}
You can inspect the type of the parameter with typeof
function openWindow(parameter){
if(typeof parameter == "string"){
window.open(parameter);
}
else if(typeof parameter == "object"){
window.open(parameter.url, parameter.title, parameter.options, parameter.replace);
}
}
You could use typeof to see if the argument is a string.
if ( typeof URL_OR_OBJECT === "string" ) {
window.open(URL_OR_OBJECT);
} else { /*...*/ }
You can to be a little cautious using this because typeof new String("hello") is "object". But I don't think too many people declare a string that way.
typeof docs on MDN: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/typeof
While I have not looked at the source code in particular, I presume it is structured something similar to this:
function css(bgcolor_or_obj, width, etc) {
var bgcolor;
if(typeof bgcolor_or_obj === 'object') {
// Expand configuration object here.
bgcolor = bgcolor_or_obj.bgcolor;
width = bgcolor_or_obj.width;
etc = bgcolor_or_obj.etc;
} else {
bgcolor = bgcolor_or_obj;
}
/* ... */
}
I am aware of no standardized way in Javascript to differentiate between an argument passed to a function as being a variable or a literal. It is more likely that the function in question is checking if the first argument is an object, as demonstrated above.
Is there a (much) better way of writing this? I am using Dojo. Maybe mixing in would work?
ajaxValidate: function(value, options){
// Set some defaults
options.ajaxInvalidMessage = options.ajaxInvalidMessage || "Value not allowed";
options.ajaxStore = options.ajaxStore || null;
options.ajaxFilterField = options.ajaxFilterField || 'name';
There doesn't seem to be a ||= operator and this looks very verbose to me...
The method you show:
obj.prop = obj.prop || "default";
...is a pretty common idiom, but personally I find an if statement more descriptive even though it actually adds a couple of characters:
if (!obj.prop) obj.prop = "default";
If you want a more general way to do it you can write a function something like the following (I'm not familiar with Dojo - perhaps it already has such a function):
function addDefaults(obj, defaults) {
for (prop in defaults)
if (!obj.hasOwnProperty(prop))
obj[prop] = defaults[prop];
}
Which you'd then use in your code snippet as:
ajaxValidate: function(value, options){
addDefaults(options, {
ajaxInvalidMessage : "Value not allowed",
ajaxStore : null,
ajaxFilterField : 'name'
});
Note that in my addDefaults() I'm using the hasOwnProperty() method which tests whether the property exists in your object. This is subtly different to the || syntax you started with which tests whether the property is "truthy".
Using a mixin is a cleaner approach. That way, you can also explicitly group your defaults into a single object.
http://dojotoolkit.org/reference-guide/1.7/dojo/mixin.html
ajaxValidate: function (value, options) {
options = dojo.mixin({
ajaxInvalidMessage: "Value not allowed",
ajaxStore: null,
ajaxFilterField: "name"
}, options);
//...
}