Skip to content

Commit

Permalink
Update README.md
Browse files Browse the repository at this point in the history
  • Loading branch information
slmgc committed Apr 6, 2018
1 parent d1c9e93 commit de524b5
Showing 1 changed file with 28 additions and 37 deletions.
65 changes: 28 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Nothing

[![npm package][npm-badge]][npm] [![npm package][npm-downloads]][npm]
[![npm package][npm-badge]][npm]

**Nothing** is a chainable, callable mock object which always returns itself. You can use it instead of `null` and `undefined` values so you don't have to place safety checks all over your code. The implementation uses `Symbol` and `Proxy` behind the hood so your environment has to support it.
**Nothing** is a chainable, callable mock object which always returns itself. You can use it instead of `null` and `undefined` values so you don't have to place safety checks all over your code. The implementation uses [Symbol] and [Proxy] behind the hood which are widely supported by modern desktop and mobile browsers and can be used without a polyfill.

## How to install

Expand All @@ -20,12 +20,12 @@ const foo = Nothing
foo.bar.baz.qux().spam.ham[0].map((x) => x + 1).eggs.someFn() // returns Nothing
```

### Property accessors
### Reducing boilerplate
```js
import {Nothing} from 'nothing-mock'

// A regular function with null-checks
function someFnWithNullChecks(foo) {
function someFunctionWithNullChecks(foo) {
return foo &&
foo.bar &&
foo.bar.baz &&
Expand All @@ -34,13 +34,13 @@ function someFnWithNullChecks(foo) {
}

// There is no need to check for null/undefined if you use Nothing
function someFn(foo) {
function someFunction(foo) {
return foo.bar.baz.qux()
}

someFnWithNullChecks(null) // returns null
someFn(Nothing) // returns Nothing
someFn(null) // throws an exception
someFunctionWithNullChecks(null) // returns null
someFunction(Nothing) // returns Nothing
someFunction(null) // throws an exception
```

### JSON serialization/deserialization
Expand Down Expand Up @@ -111,25 +111,22 @@ posts.map(renderPostWithComments).join('') /* returns:
<ul></ul> // Nothing is rendered empty
</div>` */

// Changes all Nothing values to null
// Serializes an object to JSON and
// replaces all Nothing values with null
serialize({posts})
```

### Filtering and helpers
### Helper functions
```js
import {Nothing, toBool, isNothing, isSomething} from 'nothing-mock'

const list = [Nothing, true, false, null, undefined, 0, 1, NaN, '', {}, []]
list.filter(Boolean) // [Nothing, true, 1, {}, []]
list.filter(isNaN) // [undefined, NaN, {}]
list.filter(toBool) // [true, 1, {}, []]
list.filter(isNothing) // [Nothing]
list.filter(isSomething) // [true, false, 0, 1, NaN, "", {}, []]
list.filter(Number) // [true, 1]
list.filter(String) // [true, false, null, undefined, 0, 1, NaN, {}]
list.filter(toBool) // [true, 1, {}, []]
```

### A list of properties which don't return Nothing
### Properties which don't return Nothing
```jsx
import {Nothing} from 'nothing-mock'

Expand All @@ -141,7 +138,7 @@ Nothing.toString() // ""
Nothing.valueOf() // false
```

### Typecasting and gotchas
### Gotchas
```js
import {Nothing, toBool} from 'nothing-mock'

Expand All @@ -162,19 +159,17 @@ Number(Nothing) + 123 // 123
Boolean(Nothing) // true
!!Nothing // true

// Solution: Nothing can be properly converted to a falsy value
// Solution: Nothing can be converted to false
Nothing.valueOf() // false
toBool(Nothing) // false
toBool(undefined) // false
toBool(123) // true

// Gotcha: returning Nothing from a promise never resolves
// as Nothing is a thenable object
// Gotcha: returning Nothing from a promise never
// resolves as Nothing is a thenable object
somePromise
.then(() => Nothing)
.then((result) => result) // pending indefinitely

// Solution: wrapping Nothing in an object resolves the issue
// Solution: wrapping Nothing resolves the issue
somePromise
.then(() => ({result: Nothing}))
.then((result) => result) // promise resolves
Expand All @@ -187,8 +182,7 @@ Q: Proxies are slow and there is a runtime overhead. Why should I use **Nothing*
A: You should keep a few things in mind:

1. "Premature optimization is the root of all evil" - Donald E. Knuth.
1. Native implementation of Proxies will eventually get better in modern browsers.
1. Have you checked the performance of **Nothing**? Does it really impact the performance of your code that much? Is there a better algorithm to use? If it does affect the performance, you can always opt out using **Nothing** for performance-critical parts of your code.
1. Have you checked the performance of **Nothing**? Does it really impact the performance of your code? If it does, you can always opt out using **Nothing** for performance-critical parts of your code.
1. You can use **Nothing** for writing unit tests which are less likely to be performance-dependant.

Q: I believe that it's hard to understand the logic as the code will fail silently if I would use **Nothing**. I prefer to use try/catch blocks instead, e.g.:
Expand All @@ -201,7 +195,9 @@ try {
}
```

A: As for the failing silently statement, you can always check the result in cases where a function call should never return **Nothing** and then handle it properly:
A: Many functional programming languages either don't have or don't endorse the use of imperative constructs such as try/catch blocks because they introduce so-called side effects which actually make it harder to debug and reason about the code. And programs which are written in functional programming languages are considered to be less error-prone and easier to support.

You can always check the result if a function call should never return **Nothing** and then handle it properly:

```js
const someFunction = (handleNothing, arg) => {
Expand All @@ -210,18 +206,12 @@ const someFunction = (handleNothing, arg) => {
}
```

Many functional programming languages either don't have or don't endorse the use of imperative constructs such as try/catch blocks because they introduce so-called side effects which actually make it hard to debug and reason about the code. And programs which are written in functional programming languages are considered to be less error-prone and easier to support.

Q: I have to support older browsers, is there a `Proxy` polyfill?

A: Sadly, there isn't one which supports **Nothing**. But you can use **Nothing** for unit tests as Node supports Proxies.

Q: Why should I use **Nothing** if there are better alternatives like [optional chaining] or [lodash.get]?

A: Each of these solutions have their pros and cons. Your choice should depend on the use-case:

1. Optional chaining syntax would be the best choice, but it requires a transpilation step as modern browsers don't support the syntax and it might take a while before it will get into the future ECMAScript standard.
1. `lodash.get` is good for basic property chain traversal, but it requires an alien syntax and fails when there is a need to call a method somewhere in the property chain:
1. `lodash.get` is good for a basic property chain traversal, but it requires an alien syntax and fails when there is a need to call a method somewhere in a property chain:

```js
import get from 'lodash.get'
Expand All @@ -242,13 +232,13 @@ var foo = {
}
}

foo.bar.baz() // outputs "hello"
get(foo, ['bar', 'baz'])() // outputs "undefined"
foo.bar.baz() // "hello"
get(foo, ['bar', 'baz'])() // undefined

// This would be a proper solution:
var bar = get(foo, ['bar'])
var baz = get(bar, ['baz'])
baz && baz.call(bar)
baz && baz.call(bar) // "hello"

// But then it's easier to get back to the regular syntax:
foo && foo.bar && foo.bar.baz && foo.bar.baz()
Expand All @@ -267,8 +257,9 @@ A: Thanks for letting me know! Seriously, it's your choice, I am down with it.
## License
MIT

[Proxy]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Browser_compatibility
[Symbol]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility
[npm]: https://www.npmjs.org/package/nothing-mock
[npm-badge]: https://img.shields.io/npm/v/nothing-mock.svg
[npm-downloads]: https://img.shields.io/npm/dm/nothing-mock.svg
[optional chaining]: https://www.npmjs.com/package/babel-plugin-transform-optional-chaining
[lodash.get]: https://www.npmjs.com/package/lodash.get

0 comments on commit de524b5

Please sign in to comment.