Null Conditional Operators

后端 未结 4 1457
陌清茗
陌清茗 2020-12-01 12:01

C# 6.0 has just been released and has a new nice little feature that I\'d really like to use in JavaScript. They\'re called Null-conditional operators. These use a ?.<

相关标签:
4条回答
  • 2020-12-01 12:03

    Here's a quick and dirty version that works.

    String.prototype.nullSafe = function() {
        return eval('var x='+this.replace(/\?/g,';if(x)x=x')+';x');
    };
    

    Example usage:

    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
    
    0 讨论(0)
  • 2020-12-01 12:10

    Called "optional chaining", it's currently a TC39 proposal in Stage 4. A Babel plugin however is already available in v7.

    Example usage:

    const obj = {
      foo: {
        bar: {
          baz: 42,
        },
      },
    };
    
    const baz = obj?.foo?.bar?.baz; // 42
    
    const safe = obj?.qux?.baz; // undefined
    
    0 讨论(0)
  • 2020-12-01 12:23

    Js logical operators return not true or false, but truly or falsy value itself. For example in expression x && y, if x is falsy, then it will be returned, otherwise y will be returned. So the truth table for operator is correct.

    In your case you could use expression customers && customers.orders && customers.orders.Length to get length value or the first falsy one.

    Also you can do some magic like ((customers || {}).orders || {}).length (Personally, I don't like this syntax and possible garbage collection pressure as well)

    Or even use maybe monad.

    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();
    

    It's longer than all the previous approaches, but clearly shows your intentions without any magic behind.

    0 讨论(0)
  • 2020-12-01 12:25

    There are several ways to improve code readability (depending on your needs):

    1. You already use babeljs (v7 or above) and you use "Optional Chaining" babel plugin (finished proposal) (or just preset-stage-3):

      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. You already use lodash (v3.7 or greater): Use the lodash.get method:

      var length = _.get(customers, 'orders.length');
      
      // With default value (otherwise it will be `undefined`):
      var length = _.get(customers, 'orders.length', defaultLength);
      
    3. Plain 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;
      
    0 讨论(0)
提交回复
热议问题