Есть ли обработка свойств JavaScript undefined, как в шаблонах Angular2

Мне было интересно, есть ли способ сделать это:

if (app && app.object && app.object.foo) {
  alert(app.object.foo.bar);
}

Это действительно длинный и "уродливый".

Я узнал, что Angular2 имеет что-то действительно замечательное для подобных случаев. Но я думаю, что это только для шаблонов:

<div>{{this?.object?.foo?.bar}}</div>

Мне это действительно удалось выйти, потому что у меня много кода, который выглядит как первый пример. Он выполняет свою работу, но я действительно прыгаю, есть что-то более сложное.

Ответ 1

Многие языки имеют эту функциональность, некоторые называют ее safe navigation operator или даже Elvis operator (да ха-ха).

JavaScript не имеет этой функции. Если вы открыты для использования CoffeeScript, вы можете взглянуть на экзистенциальный оператор.

Но, если вы хотите сохранить JS, вы должны взглянуть на lodash.get(), который позволяет вам сделать что-то вроде этого:

var object = { 'a': [{ 'b': { 'c': 3 } }] };

_.get(object, 'a[0].b.c');
// → 3

_.get(object, 'a.b.d.c', 'default');
// → 'default'

Ответ 2

Я нашел это решение когда-нибудь в сети:

function checkNested(obj /*, level1, level2, ... levelN*/) {
  var args = Array.prototype.slice.call(arguments, 1);

  for (var i = 0; i < args.length; i++) {
    if (!obj || !obj.hasOwnProperty(args[i])) {
      return false;
    }
  obj = obj[args[i]];
 }
  return true;
}

var test = {level1:{level2:{level3:'level3'}} };

checkNested(test, 'level1', 'level2', 'level3'); // true
checkNested(test, 'level1', 'level2', 'foo'); // false

jsFiddle: https://jsfiddle.net/nikdtu/kjsxnunp/

Ответ 3

не из коробки.

var string = function(v){ return v == null? "": String(v) }  //because I usually want this behaviour

var fetch = function(path, obj){
    path instanceof Array || (path = string(path).split('.'));
    for(var i=0, v=obj; i<path.length; v = v[path[i++]])
        if(v == null) return void 0;
    return v;
}

и ваш код:

if(fetch("object.foo", app)){
    alert(app.object.foo.bar);
}

или даже более прямой:

alert( fetch("object.foo.bar", app) );
//will alert undefined, if the path could not be resolved

Аргумент порядка такой, потому что эта функция обычно находится в моем коде, поэтому я могу сделать так:

arr.map(fetch('some.deeper.path'));

Ответ 4

Немного короче (чем оригинальный) способ в JavaScript (менее читаемый)

if ((a = app) && (o = a.object) && (f = o.foo)) {
  alert(f.bar);
}

Как я его напишу в CoffeeScript (мой предпочтительный метод)

alert f.bar if f = app?.object?.foo?

Если вы действительно используете Javascript и хотите, чтобы он выглядел чистым, вы могли бы использовать что-то вроде:

function ifExist (name) { // Returns undefined if it doesn't exist
  arr = name.split('.');
  obj = eval('if (typeof '+(x = arr.splice(0,1)[0])+' !== \'undefined\') '+x);
  for (i = 0; i < arr.length; i++) {
    if (typeof obj === 'undefined') {
      return obj;
    }
    else {
      obj = obj[arr[i]];
    }
  }
  return obj
}

alert(ifExist('app.object.foo.bar'));

Поскольку он использует eval, он может представлять угрозу безопасности, если пользователь генерирует app.object.foo.bar. Он работает в простых случаях (например, тот, который вы опубликовали выше), но используя что-то вроде app.object['foo'].bar в нем, вероятно, сломает его, не добавляя некоторые проверки ввода.