The tool described in this article is a JavaScript solution for obtaining deeply nested objects and is a landing application of ES6 Proxy in conjunction with TypeScript.

Problems faced

Suppose you have an object that indicates whether the user has the reply notification set enabled

const settings = { notification: { reply: { active: true } // ... Other Settings} //... Other Settings}Copy the code

When a developer wants to extract the value of active, the most straightforward way is to do so

const isNotificationReplyActive = settings.notification.reply.activeCopy the code

This is not safe because JavaScript usually uses’ null ‘or’ undefined ‘for undefined or undeclared values, respectively

Typeof someVar === 'undefined' // let someVar = null // Declared, undefinedCopy the code

In real development, for example, for resource saving reasons, when the user has not set it, the value of notification or a deeper level is’ null ‘or’ undefined ‘instead of object.

// For example, when the reply notification is not set, it is const Settings = {notification: {// No reply}} // in this case, Settings. The notification. The reply of the value is undefined / / JS in trying to get the key on the undefined triggered when a TypeError const isNotificationReplyActive =  settings.notification.reply.active // TypeError!Copy the code

Some developers have taken this step

const isNotificationReplyActive = settings && settings.notification && settings.notification.reply && Settings. Notification. Reply. Active / / or try {const isNotificationReplyActive = Settings. The notification. The reply. The active} Catch (err) {// Error handling}Copy the code

As experienced developers know, there are many drawbacks to doing this, and I won’t go into them here.


Some utility functions were born, such as’ _. Get ‘for Lodash

import _ from 'lodash'
const isNotificationReplyActive = _.get(settings, 'notification.reply.active')Copy the code

While it ensures that a TypeError will not be thrown when a value such as’ undefined ‘or’ null ‘is encountered during attribute extraction, the disadvantage is obvious

  1. The path to the property is written as a string, and the developer cannot get IDE/ editor auto-completion and intelligent error correction.
  2. Can’t use the convenient destruct syntax
const { notification: { reply: { active } } } = settingsCopy the code


It’s like a night back before liberation.

Solution — Safe-touch

Now let’s review the image at the beginning of this article that is the hero of this article and the solution to all of these problems: Safe-touch.

safe-touchwww.npmjs.com

Here’s how to use it:

Import safeTouch from 'safe-touch' const Settings = {/*... Const touched = safeTouch(Settings) // Call it as a function, The original value of touched() === Settings // true // can also be reached by calling properties that exist on Settings directly. This process can give a smart tips and auto-complete touched. The notification. The reply. If active () / / in the beginning of the article gives examples of value is true / / can safely get attribute doesn't exist, return to undefined, . Don't throw TypeError touched. Something. Does not. The exist [Math. The random ()] () / / undefined / / support deconstruction const {notification: {reply: { active, notExistingKey } } } = touched active() // true notExistingKey() // undefinedCopy the code

Smart tips are available in modern tools like VS Code:


Automatic completion is supported

Support the deconstruction

performance

I did a set of simple performance tests on NodeJS V8.9.1 and V10.7.0, and the results are in this GIST.

The code for the test is here.

Let me summarize the test results

  1. The performance of safe-touch is lower than that of try almost exclusively when the deep attributes are successfully fetched at more than five nested levels and all of the attributes are present… The catch. Creating many Proxy instances costs more than creating a try… Catch block but does not trigger error overhead.
  2. If an error occurs when an attribute is fetched from a property that does not exist, then safe-touch performs a try… Catch dozens of times.
  3. There are several other similar tools in the community, many of them via try… Catch implements, so safe-Touch may be a better choice for an integrated development experience.

The test also found that the performance of Proxy under NodeJS V10 is much better than v8, compared to about 5 times.

My test script was run 100,000 times, and it probably wouldn’t make a difference in user experience to do either of these in a real-world application. But either way, it’s not as good as the original chain, so I’m looking forward to it. The formalization of the. Operator, even if it does nothing more than convert to an equivalent chain-form expression, would be nice.

Babel already offers? The operator
@babel/plugin-proposal-optional-chainingIf you are interested, you can play.

Another counter-intuitive phenomenon is that at the end of the test results, the deep chain retrieve is faster than the deepRetrieve, i.e. A && a.b &&… && A.B.C.D.E.F.G.H.I way faster than direct A.B.C.D.E.F.G.H.I (double)! This is amazing, maybe there is a bug in my test script, or there is a hack in the engine, I will study it again sometime, which will probably be the subject of my next article.

Source code is here, welcome Star/issue:

EnixCoda/safe-touchgithub.com