3

So now I nailed down the basics of javascript and I'm ready to get into the more intermediate arts of coding with "style". I'm trying to write easy maintainable code. The idea is to make a function work even if one of the object properties in use is not available by creating fallbacks. Problem is if I access the properties often then I would have to create ternary conditionals that create a simple for each accessed property. Right now you can see I'm only accessing object.a. I could of course store all the accessing of properties:

Idea 1

var a = (object.hasOwnProperty(a) ? object.a : a)
var b ...
var c ...

idea 2:

var a = (object['a'] ? object.a : a)

idea 3:

    var a = object['a'] ? object.a : a

Idea 3:

(object.hasOwnProperty(a) ? var a = object.a : var a = 1);

Idea 4:

Switch statements?

At last:

object = {
// a: 1,
b: 2,
c: 3,
}
// normal vars in case one of the properties do not exist
var a = 1,
    b = 2,
    c = 3;

function x(){
var a = 1;

object.a * 10

if (object.a == not exist || (object.b == not exist|| (object.c == not exist)

then treat all non existing object properties accessed to as normal variables by doing:
convert object.a --> a

{

Asperger
  • 3,064
  • 8
  • 52
  • 100
  • 1
    What about using [a `merge()` function](http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically), having two objects, a `defaults` object and a `target` object. Your merge would favor the `target` objects properties (not overriding them if they have values). – Cᴏʀʏ Sep 16 '15 at 18:48
  • if you don't need to block inherited or falsy values, `in` is simple: `a= "a" in obj ? obj.a : false;` – dandavis Sep 16 '15 at 18:50
  • @Cᴏʀʏ please feel free to post an answer with some examples. It sounds very interesting. Would it not create more "code" though? – Asperger Sep 16 '15 at 18:54
  • 1
    One useful utility function wouldn't hurt anything. If your application makes appropriate use of it. You would essentially have: `var myObject = { ... }; var defaults = { ... }; myObject = merge(myObject, defaults);` – Cᴏʀʏ Sep 16 '15 at 18:57
  • @Cᴏʀʏ what is your opinion about the solution below? – Asperger Sep 16 '15 at 18:59
  • My opinion is that I don't like how verbose it is -- you would need to call `getOrDefault()` for each individual property you want to handle. Maybe that's what you want, but a "merge" utility function abstracts that away and takes care of the entire object for you. If you're using jQuery at all, take a look at its `extend()` method. – Cᴏʀʏ Sep 16 '15 at 19:02
  • @CORY I only use javascript. and I have to agree with you. Merge does seem more elegant – Asperger Sep 16 '15 at 19:07
  • 1
    @Asperger: I provided a sample in an answer, but I did not include an example from the other answer I linked. – Cᴏʀʏ Sep 16 '15 at 19:08
  • @Cᴏʀʏ I posted a new question since im very curious. Maybe you are interested in having a look as well http://stackoverflow.com/questions/32618975/creating-different-delays-for-animations-on-first-and-second-click-chaining-de – Asperger Sep 16 '15 at 21:24

3 Answers3

7

The ternary operator is a great way to do this. It allows you the ability to evaluate any expression you like to determine whether your candidate value is appropriate or whether you should use a fallback.

Examples...

// Use fallback if candidate is not "truthy"
var result = obj.a ? obj.a : "fallback";

// Use fallback if candidate is undefined
var result = obj.a !== undefined ? obj.a : "fallback";

// Use fallback if candidate is not defined on the object (whether or not it exists in the prototype chain)
var result = obj.hasOwnProperty(a) ? obj.a : "fallback";

You need to decide what condition you'd like to use a fallback value. Once you decide, wrap it in a function. Or make several similar functions which use different conditions.

Here's a function which checks to see if the candidate value is undefined and returns a fallback value.

function getOrDefault(candidate, fallback) {
  if (typeof candidate === "undefined") {
    return fallback;
  }
  return candidate;
}


// Example 1
var b = "alternate value";
var obj = { foo: "value" };

var result = getOrDefault(obj.a, b);
// result -> "alternate value";


// Example 2
var b = "alternate value";
var obj = { a: false };

var result = getOrDefault(obj.a, b);
// result -> false;

Also worth looking into is lodash's get function. Allows you to check for the existence of a property (even deeply nested properties) and allows you to specify a fallback.

jessegavin
  • 74,067
  • 28
  • 136
  • 164
  • 1
    what about `toString()` and other non-own properties? they don't existing on the object, yet will still pass un-defaulted through your function... i'm afraid `hasOwnProperty()` is a must for reliable object handling... also, you don't need the `typeof`, you can just quickly compare to undefined: `if(candidate===undefined){`, since `candidate` will always be defined, even if the value is undefined... `typeof` is really only _needed_ when the lexical name might be unknown, in which case it prevents a refError. – dandavis Sep 16 '15 at 18:54
  • +1, I like the idea of using `undefined` instead of `hasOwnProperty` for this purpose. This way you can supply not only member values, but optional function parameters as well. – Tamas Hegedus Sep 16 '15 at 18:57
  • @hege_hegedus indeed. I think we can also use void(0). – Asperger Sep 16 '15 at 18:58
  • What is your opinion about CORIES solution using merge? – Asperger Sep 16 '15 at 18:58
  • merge can be handy for coding lots of setting defaults, but it's also fairly slow to perform. Object initialization typically doesn't happen often, so the speed penalty is typically negligible. then again, it is one more piece of code to ship. you can use `$.extend()` or another extend as a merge() – dandavis Sep 16 '15 at 19:02
6

ES6 provides nice facilities for doing what you want. The simplest example is

var { a = 1, b = 2 } = obj;

This uses destructuring assignment with defaults. The property a on obj is retrieved and assigned to variable a, but it if doesn't exist, a takes on the value 1.

  • what about the op that mentioned using prototype methods of dealing with this? – Asperger Sep 16 '15 at 20:17
  • Your method seems cleaner and smaller. So many people giving good advices all with their pros and cons. I have no idea which to adopt – Asperger Sep 16 '15 at 20:18
  • @Asperger not getting your question--could you please amplify? –  Sep 16 '15 at 20:18
  • Sorry. I mean, your solutions seems very elegant. Now im struggling to decide which one to choose, yours or CORY's. I mean in terms of maintainability and flexibility and of course performance – Asperger Sep 16 '15 at 20:22
  • Ok support is really bad – Asperger Sep 16 '15 at 20:26
  • Performance is an issue only if you intend to do this one million times. An ES6 transpiler will compile this code into something like `var a = obj.a === undefined ? 1 : obj.a;`, so if you do worry about performance it's no different than writing out something like that yourself. Cory's solution is an implementation of Underscore's `_.default` (with the arguments reversed, and returning a new object instead of copying the defaults onto missing properties in the input). Either is fine. If you have access to an ES6 environment, destructuring is cleaner and more readable in my opinion. –  Sep 16 '15 at 20:27
1

Expanding on my comments about an "extend" or "merge" function, let's take a look at the following (from here):

var extend = function ( defaults, options ) {
    var extended = {};
    var prop;
    for (prop in defaults) {
        if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
            extended[prop] = defaults[prop];
        }
    }
    for (prop in options) {
        if (Object.prototype.hasOwnProperty.call(options, prop)) {
            extended[prop] = options[prop];
        }
    }
    return extended;
};

// Your "fallback" object values
var defaults = {
  a: 1,
  b: 2
};

// Your target object
var myObject = {
  b: 4,
  c: 8
};

// one line to override a set of defaults, producing a final object
console.log(extend(defaults, myObject)); // Object { a: 1, b: 4, c: 8 }
Cᴏʀʏ
  • 105,112
  • 20
  • 162
  • 194
  • Prototype is new to me. Want to tell me what it does? I could google it but would like to hear it from you as you seem very knowledgable – Asperger Sep 16 '15 at 19:08
  • 1
    Every object in JavaScript has a `prototype` -- which to me is getting into advanced level JavaScript. Since we're discussing intermediate JavaScript topics, I'll leave that as an exercise for you. To do it justice, it would take a lot more text than can fit in a comment here. – Cᴏʀʏ Sep 16 '15 at 19:12
  • Ok then. Leave it as my homework ; ) I will accept your answer as you did a lot and it helped – Asperger Sep 16 '15 at 19:25
  • sorry, may I ask if this is a good article? http://javascriptissexy.com/javascript-prototype-in-plain-detailed-language/ – Asperger Sep 16 '15 at 19:27
  • @CORY what does .call do? – Asperger Sep 16 '15 at 20:00
  • 1
    That article is pretty good. `.call` is what's invoking the prototype method. `.call` allows you to specify what `this` is within the method. – Cᴏʀʏ Sep 16 '15 at 20:34
  • I feel that using ternary operator keeps it more elegant and faster for smaller apps than have roughly half the more code that your solution but for apps that require a lot of functions I see your solution to be extremely handy – Asperger Sep 16 '15 at 20:54
  • @Asperger: Sounds fair to me! :) – Cᴏʀʏ Sep 16 '15 at 20:55
  • I accepted the other one answer up there but yours is really the smarter way in the long term. Really great info and upvoted your comment and answer – Asperger Sep 16 '15 at 20:56