Подтвердить что ты не робот

Нулевые условные операторы

С# 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;
}
4b9b3361

Ответ 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

Есть несколько способов улучшить читаемость кода (в зависимости от ваших потребностей):

  1. Вы уже используете (версия 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"] }
    
  2. Вы уже используете (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);
    
  3. Обычный 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