问题
Is there a null
-safe property access (null propagation / existence) operator in ES6 (ES2015/JavaScript.next/Harmony) like ?.
in CoffeeScript for example? Or is it planned for ES7?
var aThing = getSomething()
...
aThing = possiblyNull?.thing
This will be roughly like:
if (possiblyNull != null) aThing = possiblyNull.thing
Ideally the solution should not assign (even undefined
) to aThing
if possiblyNull
is null
回答1:
Update (2019-06-27): Seems people are still finding this, here's the current story:
- Optional Chaining specification (Stage 2): https://github.com/tc39/proposal-optional-chaining
- Babel v7 Plugin: https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining
Update (2017-08-01): If you want to use an official plugin, you can try the alpha build of Babel 7 with the new transform. Your mileage may vary
https://www.npmjs.com/package/babel-plugin-transform-optional-chaining
Original:
A feature that accomplishes that is currently in stage 1: Optional Chaining.
https://github.com/tc39/proposal-optional-chaining
If you want to use it today, there is a Babel plugin that accomplishes that.
https://github.com/davidyaha/ecmascript-optionals-proposal
回答2:
It's not as nice as the ?. operator, but to achieve a similar result you could do:
user && user.address && user.address.postcode
Since null
and undefined
are both falsy values (see this reference), the property after the &&
operator is only accessed if the precedent it not null or undefined.
Alternatively, you could write a function like this:
function _try(func, fallbackValue) {
try {
var value = func();
return (value === null || value === undefined) ? fallbackValue : value;
} catch (e) {
return fallbackValue;
}
}
Usage:
_try(() => user.address.postcode) // return postcode or undefined
Or, with a fallback value:
_try(() => user.address.postcode, "none") // return postcode or a custom string
回答3:
No. You may use lodash#get or something like that for this in JavaScript.
回答4:
Vanilla alternative for safe property access
(((a.b || {}).c || {}).d || {}).e
The most concise conditional assignment would probably be this
try { b = a.b.c.d.e } catch(e) {}
回答5:
No, there is no null propagation operator in ES6. You will have to go with one of the known patterns.
You may be able to use destructuring, though:
({thing: aThing} = possiblyNull);
There are many discussions (e.g. this) to add such an operator in ES7, but none really took off.
回答6:
Going by the list here, there is currently no proposal to add safe traversal to Ecmascript. So not only is there no nice way to do this, but it is not going to be added in the forseeable future.
回答7:
A safe deep get method seems like a natural fit for underscore.js but there the issue is avoiding string programming. Modifying @Felipe's answer to avoid string programming (or at least pushes edge cases back to the caller):
function safeGet(obj, props) {
return (props.length==1) ? obj[keys[0]] :safeGet(obj[props[0]], props.slice(1))
}
Example:
var test = {
a: {
b: 'b property value',
c: { }
}
}
safeGet(test, ['a', 'b'])
safeGet(test, "a.b".split('.'))
回答8:
// Typescript
static nullsafe<T, R>(instance: T, func: (T) => R): R {
return func(instance)
}
// Javascript
function nullsafe(instance, func) {
return func(instance);
};
// use like this
const instance = getSomething();
let thing = nullsafe(instance, t => t.thing0.thing1.thingx);
回答9:
I know this is a JavaScript question, but I think Ruby handles this in all of the requested ways, so I think it's a relevant point of reference.
.&
, try
, and && have their strengths and potential pitfalls. A great run down of those options here: http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
TLDR;
The Rubyists conclusion is that dig
is both easier on the eyes and a stronger guarantee that a value or null
will be assigned.
Here's a simple imeplementation in TypeScript:
export function dig(target: any, ...keys: Array<string>): any {
let digged = target
for (const key of keys) {
if (typeof digged === 'undefined') {
return undefined // can also return null or a default value
}
if (typeof key === 'function') {
digged = key(digged)
} else {
digged = digged[key]
}
}
return digged
}
This can be used for any depth of nesting and handles functions.
a = dig(b, 'c', 'd', 'e');
foo = () => ({});
bar = dig(a, foo, 'b', 'c')
The try
approach is equally nice to read in JS, as shown in previous answers. It also does not require looping, which is one drawback of this implementation.
回答10:
I thought this question needed a bit of a refresh for 2018. This can be done nicely without any libraries using Object.defineProperty()
and can be used as follows:
myVariable.safeGet('propA.propB.propC');
I consider this safe (and js-ethical) because of the writeable
and enumerable
definitions now available for the defineProperty
method of Object
, as documented in MDN
function definition below:
Object.defineProperty(Object.prototype, 'safeGet', {
enumerable: false,
writable: false,
value: function(p) {
return p.split('.').reduce((acc, k) => {
if (acc && k in acc) return acc[k];
return undefined;
}, this);
}
});
I've put together a jsBin with console output to demonstrate this. Note that in the jsBin version I've also added a custom exception for empty values. This is optional, and so I've left it out of the minimal definition above.
Improvements are welcomed
来源:https://stackoverflow.com/questions/32139078/null-safe-property-access-and-conditional-assignment-in-es6-2015