Нулевые условные операторы
С# 6.0 только что был выпущен и имеет новую приятную небольшую функцию, которую я бы очень хотел использовать в JavaScript. Они называются Нулевыми условными операторами. Они используют синтаксис ?.
или ?[]
.
Что это значит, по сути, вы можете проверить, что у вас нет объекта null
, прежде чем пытаться получить доступ к свойству. Если объект null
, тогда вы получите null
как результат доступа к вашему ресурсу.
int? length = customers?.Length;
Итак, здесь int
может быть нулевым, и примет это значение, если customers
равно null. Что еще лучше, так это то, что вы можете связать их:
int? length = customers?.orders?.Length;
Я не верю, что мы можем сделать это в JavaScript, но мне интересно, какой самый простой способ сделать что-то подобное. Обычно я считаю, что цепочка if
трудно читать:
var length = null;
if(customers && customers.orders) {
length = customers.orders.length;
}
Ответы
Ответ 1
Называемый "необязательной цепочкой", он в настоящее время является предложением TC39 на этапе 3. Тем не менее, плагин Babel уже доступен в версии 7.
Пример использования:
const obj = {
foo: {
bar: {
baz: 42,
},
},
};
const baz = obj?.foo?.bar?.baz; // 42
const safe = obj?.qux?.baz; // undefined
Ответ 2
Логические операторы Js возвращают не true
или false
, а truly
или falsy
значение. Например, в выражении x && y
, если x
ложно, оно будет возвращено, иначе y
будет возвращено. Таким образом, таблица истинности для оператора верна.
В вашем случае вы можете использовать выражение customers && customers.orders && customers.orders.Length
falsy
чтобы получить значение length
или первое falsy
.
Также вы можете сделать что-то магическое, например ((customers || {}).orders || {}).length
(Лично мне не нравится этот синтаксис и возможное давление сборки мусора)
Или даже использовать, maybe
монаду.
function Option(value) {
this.value = value;
this.hasValue = !!value;
}
Option.prototype.map = function(s) {
return this.hasValue
? new Option(this.value[s])
: this;
}
Option.prototype.valueOrNull = function() {
return this.hasValue ? this.value : null;
}
var length =
new Option(customers)
.map("orders")
.map("length")
.valueOrNull();
Это дольше, чем все предыдущие подходы, но ясно показывает ваши намерения без какой-либо магии позади.
Ответ 3
Есть несколько способов улучшить читаемость кода (в зависимости от ваших потребностей):
-
Вы уже используете babeljs (версия 7 или выше) и готовы использовать плагин babel "Необязательная цепочка" (который по-прежнему является предложением этапа 1):
const length = customers?.orders?.Length;
// With default value (otherwise it will be 'undefined'):
const length = customers?.orders?.Length || defaultLength;
// File: .babelrc
{ "plugins": ["@babel/plugin-proposal-optional-chaining"] }
-
Вы уже используете lodash (v3.7 или выше): используйте метод lodash.get
:
var length = _.get(customers, 'orders.length');
// With default value (otherwise it will be 'undefined'):
var length = _.get(customers, 'orders.length', defaultLength);
-
Обычный JavaScript:
var length = customers && customers.orders && customers.orders.length;
// With default value (otherwise it may be whatever falsy value "customers" or "customers.orders" might have):
var length = (customers
&& customers.orders
&& customers.orders.length) || defaultLength;
Ответ 4
Здесь быстрая и грязная версия, которая работает.
String.prototype.nullSafe = function() {
return eval('var x='+this.replace(/\?/g,';if(x)x=x')+';x');
};
Пример использования:
var obj = {
Jim: 1,
Bob: { "1": "B", "2": "o", "3": "b" },
Joe: [ 1, 2, 3, { a: 20 } ]
};
obj.Joe[3].a // 20
"obj.Joe[3]?.a".nullSafe() // 20
obj.Joe[4].a // Error: Can't read 'a' from undefined
"obj.Joe[4].a".nullSafe() // Error: Can't read 'a' from undefined
"obj.Joe[4]?.a".nullSafe() // undefined
obj.Jack[3].a.b // Error: Can't read '3' from undefined
"obj.Jack[3].a.b".nullSafe() // Error: Can't read '3' from undefined
"obj.Jack?[3].a.b".nullSafe() // undefined