检查属性是否存在的速记函数

4

你们能帮我创建一个简写函数来判断对象属性是否存在吗?99%的情况下,我想用它来检查返回的JSON对象是否包含指定的属性。请注意,不存在任何父属性,甚至JSON对象本身被定义的保证。

我考虑使用如下方式:

function propertyExists(<property>) {
    // property is something like data.property.property
    return typeof(data) !== "undefined" && typeof(data.property) !== "undefined" && typeof(data.property.property) !== "undefined";
}

我不知道如何以动态方式编写来检查所有父属性。另外,输入参数应该只是对"data.property.property"的引用,而不是字符串,因此我也不知道如何在其中找到父属性。


1
一个 in 测试可能比 typeof 更好。最近这个问题被问得很多,很快就会有人发布链接。 - RobG
3个回答

4

这里有一个我在项目中使用的函数,可以告诉你一个属性是否被定义(包括它的所有父属性,如果有的话):

function isDefined(target, path) {
    if (typeof target != 'object' || target == null) {
        return false;
    }

    var parts = path.split('.');

    while(parts.length) {
        var branch = parts.shift();
        if (!(branch in target)) {
            return false;
        }

        target = target[branch];
    }

    return true;
}

应该像这样使用:

var data = { foo: { bar: 42 } };
isDefined(data, "foo"); // true
isDefined(data, "foo.bar"); // true
isDefined(data, "notfoo"); // false
isDefined(data, "foo.baz"); // false

您可以轻松地调整此代码以返回值本身(或null),而不是true/false

更新:在阅读问题评论后,我搜索了JavaScript in运算符并将typeof测试替换为它。现在代码已经按照原意编写。


我喜欢这个问题 - 我如何假设总是应该在目标“foo”上作为对象检查isDefined(“foo.bar”)?如果foo = {},则应在isDefined(“foo”)上工作,如果foo = {a:{b:{}}},则应在isDefined(“foo.a.b”)上工作。 - Yonder
@Yonder:不确定你的意思。可能是通过将“=== 'undefined'”更改为“=== 'object'”来实现。 - Jon
我的意思是将签名更改为函数 isDefined(path) -> 从路径中推导目标(在“foo.bar”中的“foo”)。 - Yonder
@Jon:好的,我现在明白这个签名的原因了。感谢你的解释。 - Yonder
@RobG:是哪个浏览器给你这个问题的? - Jon
显示剩余7条评论

2

JSON属性可以包含点,例如{"a.b": 42},这使得点字符串不适用于对象的“深度引用”。

isDefined({"a.b": 42}, 'a.b') // false
isDefined({"a": 42}, 'a.b') // TypeError

因此,对于引用或索引来说,数组可能是更好的选择。

function hasProperty(value, index) {
    if (index instanceof Array) {
        return index.length === 0 ||
            (hasProperty(value, index[0])
                && hasProperty(value[index[0]], index.slice(1)));
    }
    return value.hasOwnProperty(index);
}

它的使用方法如下:

hasProperty(42, []); // true
hasProperty(42, ['a']); // false
hasProperty(42, ['a']); // false
hasProperty({a: 42}, 'a'); // true
hasProperty({a: 42}, ['a']); // true
hasProperty({a: 42}, ['a', 'b']); // false
hasProperty({a: {b: 42}}, ['a', 'b']); // true
hasProperty({"a.b": 42}, ['a.b']); // true
hasProperty([1,2,3], 2); // true
hasProperty([1,2,3], 3); // false
hasProperty({a: {b: [1,2,3]}}, ['a', 'b', 2]); // true

请注意,由于使用原型函数hasOwnPropertyhasProperty会忽略来自原型的属性。

1

目前我找不到其他的帖子,但我相信以下内容是正确的:

function checkAccess(obj, path) {
  var path = path.split('.');
  var prop;

  for (var i=0, iLen=path.length; i<iLen; i++) {
    if (obj !== null && typeof obj == 'object') {
      prop = path[i];

      if (prop in obj) {
        obj = obj[prop];
      }
    } else {
      return false
    }
  }
  return true;
}

var o = {foo:{bar:null}};

alert(checkAccess(o, 'foo.bar')); // true
alert(checkAccess(o, 'foo.bar.baz')); // false

请注意,这对于 JSON 应该是没问题的,但如果涉及到宿主对象,那么一切都不确定了,因为在这种情况下,typeof 不能保证返回 object(或任何其他值)。如果您需要测试宿主对象,最可靠的解决方案可能是使用 try..catch,除非您确信被测试的对象将返回预期结果。
try {
  alert( o.foo.bar.baz);
} catch(e) {
  alert( 'oops');
}

如果您想使用单个参数,那么我会假设基础对象是全局属性:
var checkAccess = (function(global) {
  return function (expr) {
    var path = expr.split('.');
    var obj, prop;

    if (path.length) {
      obj = global[path.shift()];

      for (var i=0, iLen=path.length; i<iLen; i++) {
        if (obj !== null && typeof obj == 'object') {
          prop = path[i];

          if (prop in obj) {
            obj = obj[prop];
          }
        } else {
          return false
        }
      }
      return true;
    }
    return false;
  }
}(this));

编辑

请注意,上述内容仅表示尝试访问该路径不会返回错误,但并不意味着它将返回一个值(它可能是未定义的、空的或其他内容)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接