1

The value null represents the intentional absence of any object value.

In APIs, null is often retrieved in place where an object can be expected but no object is relevant.

MDN (emphasis mine)

TL;DR: Why does null.content throw an error but false.content returns undefined?

In my project’s API, I have methods allowing the user to set and get an icon of a Page.

Page.prototype.setIcon = function (key) {
  this.icon = database.find(function (item) { return item.content === key })
}
Page.prototype.getIconContent = function () {
  return this.icon.content
}

A string is given as the argument, and the program searches the database for an object whose content property equals the given argument. If no such object is found, the find() function will return undefined. So this.icon will be set to undefined.

This is not good because when the user calls getIconContent(), they will receive an error, because the program is trying to find the content property of undefined.

What I want to happen is that if no such object in the database is found, setIcon() will set this.icon to a falsy value that will not throw an error when attempting to get the content property. So I tried using OR null, as it is an object.

Page.prototype.setIcon = function (key) {
  this.icon = database.find(function (item) { return item.content === key }) || null
}

But this didn’t work because trying to get null.content will still throw an error. So then I tried OR false and it works the way I want.

Page.prototype.setIcon = function (key) {
  this.icon = database.find(function (item) { return item.content === key }) || false
}

So when a user calls setIcon() with no argument, and then tries getIconContent(), they will not receive an error, because false.content doesn’t throw an error; it returns undefined, which is the behavior I’m looking for.

My question is, why is false the correct usage, when clearly the expected type of icon should be an object? I would think null would be more “semantically correct,” but it throws an error.


PS: In addition to false, these are other falsy values that don’t throw errors:

var a = 0;   a.content // returns `undefined`
var b = NaN; b.content // returns `undefined`
var c = '';  c.content // returns `undefined`

The empty object {}, even though it is truthy

var d = {}; d.content // returns `undefined`

undefined throws an error as expected

var e; e.content // throws an error "Uncaught ReferenceError: e is not defined"
Community
  • 1
  • 1
chharvey
  • 8,580
  • 9
  • 56
  • 95
  • 1
    Duck typing is weird, and null in javascript is even weirder. I would just create a dummy object with all the needed properties, and return that instead of null. – Tamas Hegedus Jun 24 '16 at 15:01
  • Note that the link you point to as evidence of `null` being an object explicitly says it is not an object. `typeof null === 'object'`, but `null` is not an object. The answers note that this is due to the spec saying that's what `typeof` should return, irrespective of its actual implementation. `null` is actually a primitive value, just like `object`. – Heretic Monkey Jun 24 '16 at 15:08
  • Concur with @Mike McCaughan. This question is probably a duplicate of the page you linked: [http://stackoverflow.com/questions/18808226/why-is-typeof-null-object]. – Juan Tomas Jun 24 '16 at 15:10
  • I see. But even though `typeof null` should be `'null'` (and not `'object'` as that’s a bug), I stand by my original question of why `false` stands a better chance than `null` of testing for properties, when `null` is explicitly said to be used *where an object is expected*. @JuanTomas I agree that question is related, but I don't think this is an exact duplicate. – chharvey Jun 24 '16 at 15:13
  • 1
    Well, checking for `null` when you expect an object reference is kind of standard for most programming languages. Search SO for NullReferenceException and start wading... – Heretic Monkey Jun 24 '16 at 15:21
  • @MikeMcCaughan Ah, good thinking. So you're saying maybe my flaw is rather in `Page.prototype.getIcon`? – chharvey Jun 24 '16 at 15:23
  • 2
    It's arguably a defect in the language. For some obscure historical reason, `typeof null` equals `object`, even though `null` is really nothing, no object. So why does `false` fare better in this context? Because `false` is a thing, a boolean. Using `false` in this way seems kind of a code smell to me. Probably the most readable solution is a ternary operator in `..getIcon()` to explicitly check for `null`, as you and @Mike McCaughan suggest. – Juan Tomas Jun 24 '16 at 15:29
  • Agreed. If one of you could post an answer, that would be extremely helpful! – chharvey Jun 24 '16 at 15:30
  • In your `getIcon` you should check if `this.icon` exists and the return its content. But I would expect that is should be either called `getIconContent`, return `this.icon` or that your `this.icon` should be actually the value of `this.icon.content`. – t.niese Jun 24 '16 at 15:31
  • @t.niese `this.icon` needs to be an actual object because of other methods not shown in this question. – chharvey Jun 24 '16 at 15:32
  • 1
    That might be true but then I would name `this.icon` differently. Because it is not logical why `this.getIcon` returns something different then the `this.icon`. – t.niese Jun 24 '16 at 15:35

2 Answers2

2

The trick with null is that, while typeof null === 'object', null itself is actually its own type of primitive. See this question for a more in-depth discussion, including excerpts from the relevant specification.

null is a valid thing to return for when an object is expected, but it is incumbent upon consumers to check the return value for the null value. For instances where it goes wrong, simply see the tag and the numerous questions about handling that scenario.

Whether or not you should be returning null, undefined, or some other value from your getIcon method is really a design question, prone to opinions rather than facts, so I'll refrain from offering mine.

Community
  • 1
  • 1
Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
1

false is a boolean. Booleans are boxable (like numbers and strings) because there is a Boolean constructor in the language. When you try to access false.content, it becomes an object, nearly (but not quite, because it's still of type "boolean") what you'd get from new Boolean(false). Being an object, it can have properties. It's just that booleans have no useful properties (unlike strings which have length etc) so you never see them used that way.

On the other hand, there is no Null constructor that could box the value null of type "null" (or indeed type "object" in older versions of ECMAScript). So there is no valid way to interpret null.content.

Touffy
  • 6,309
  • 22
  • 28