【问题标题】:Proper getting of an object property in Javascript在 Javascript 中正确获取对象属性
【发布时间】:2016-02-14 00:59:50
【问题描述】:

我目前正在一个大型 Javascript 代码库中工作,代码库中到处都是诉诸流控制异常的代码

function getChecklistUrl() {
    try {
        return dataLayerObject.config.checklist;
    } catch (e) {
        try {
            console.error('dataLayer', e);
        } catch (ignore) {}
    }
}

我可能更喜欢条件逻辑,例如相同功能的这种实现

function getChecklistUrl() {
    if(typeof dataLayerObject == 'object'        &&
       'config' in dataLayerObject               &&
       typeof dataLayerObject.config == 'object' &&
       'checklist' in dataLayerObject.config     &&
       typeof dataLayerObject.config.checklist == 'object') {
        return dataLayerObject.config.checklist;
    }
    return null;
}

虽然后者感觉冗长,但当然可以编写一个辅助函数来减少此类检查的样板文件。

前者是 Javascript 的惯用语吗?后者是否脆弱(跨浏览器/场景)并且最好留给try/catch?还是前者只是懒惰的证据?

编辑

这些对象被假定为“普通”对象,例如var obj = {},所以我认为我不关心这里的原型链。

【问题讨论】:

  • 'config' in dataLayerObject 如果直接跟在typeof dataLayerObject.config == 'object' 后面是多余的(否则是undefined
  • 另外,还有一点需要注意,typeof null === 'object' 在 ES5 和下面
  • @Amit 不正确,假设var dataLayerObject = { config: null }; 然后'config' in dataLayerObject 评估为true,随后检查dataLayerObject.config.something 将引发异常...
  • @nem 这是一个非常好的观点!
  • 你是想只在对象上查找属性,还是在原型链上也一样?从您的代码(通过使用in)看来,您也在寻找原型方法?

标签: javascript flow-control


【解决方案1】:

在 javascript 中检查对象属性的正确方法是 Object.hasOwnProperty() 方法。

示例:

var Person = {
  first_name: 'Fred',
  last_name: 'Flintstone'
};

if ( 'object' === typeof Person && Person.hasOwnProperty('first_name' ) {
  window.alert('the property exists!');
}

编辑

要检查嵌套对象的属性,您可以尝试以下方法:

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

【讨论】:

  • 错了。 hasOwnProperty() 忽略原型链。
  • 谢谢。最后,我发现@nem 的回答更有帮助,但感谢您的回答并给了您一个支持。
【解决方案2】:

首先,您不需要同时检查两者 property in object && typeof obj[property] == 'object',您只能使用typeof 来处理这两个检查。原因是,如果 obj.property 不存在,typeof 将返回 undefined

因此,您可以通过编写一个检查某物是否为对象的小型实用函数来模块化您的方法:

function isObject(o) {
    return typeof o == 'object' && o !== null; // take care of the corner case that typeof null == 'object'
}

然后只需在必要的对象链上使用它,通过检查其所有拥有的对象是否存在来查找属性:

function getChecklistUrl() {
    if(isObject(dataLayerObject) && 
       isObject(dataLayerObject.config) &&  
       isObject(dataLayerObject.config.checklist)) { 

        return dataLayerObject.config.checklist;
    }
    return null;
}

var dataLayerObject = {
    config: {
         checklist: ['array of stuff']
    }
}

function isObject(o) {
  return typeof o == 'object' && o !== null;
}

function getChecklistUrl() {
  if (isObject(dataLayerObject) &&
    isObject(dataLayerObject.config) &&
    isObject(dataLayerObject.config.checklist)) {

    return dataLayerObject.config.checklist;
  }
  return null;
}

console.log(getChecklistUrl()[0]);

这让代码更有条理,更容易阅读,恕我直言。

您还可以为对象执行类似 getter 的操作,该对象采用点分隔的字符串并返回属性,如果属性不存在则返回 null:

function getObjProperty(obj, propString) {
    if(!isObject(obj) || !propString || typeof propString != 'string') {
        return null;                                 // make sure obj is an object and propString is a non-empty string
    } 

    var props = propString.split('.');
    for (var i = 0, l = props.length; i < l; i++) {
        var p = props[i];
        if(!isObject(obj[p])) { return null; }       // if current property isn't an object, return null
        obj = obj[p];                                // otherwise update the object to the next one down the property chain
    }
    return obj;
}

你会像这样使用它:getObjProperty(dataLayerObject, 'config.checklist');

var dataLayerObject = {
  config: {
    checklist: ['array of stuff']
  }
};

function isObject(o) {
  return typeof o == 'object' && o !== null;
}

function getObjProperty(obj, propString) {
  if (!isObject(obj) || !propString || typeof propString != 'string') {
    return null;
  }

  var props = propString.split('.');
  for (var i = 0, l = props.length; i < l; i++) {
    var p = props[i];
    if (!isObject(obj[p])) {  // 
      return null;
    } // if current property isn't an object, return null
    obj = obj[p]; // otherwise update the object to the next one down the property chain
  }
  return obj;
}

console.log(getObjProperty(dataLayerObject, 'config.checklist'));

或者您可以通过fairly straightforward recursive method 实现此目的

注意

上面的例子也检查了原型链。如果你不想这样,你应该在检查一个属性是否是一个对象时使用hasOwnProperty来检查这个属性是否存在于测试对象上。

Prefix's answer 展示了这种方法的一种变体。

【讨论】:

  • 这与我开始编写的代码非常相似。问题更多是围绕检查组件,这是原始标题的来源。我非常感谢您的回答和努力。
  • 没问题的朋友,很高兴帮助:)
【解决方案3】:

如果你想让它变短,你可以做这样的事情,但它不会检查 dataLayer 是否是一个对象,所以如果它是一个带有配置和清单的函数,它也会返回它。但由于它是自定义对象,因此可能不是问题。如果需要,您可以添加“in”或“hasOwnProperty”检查,或者如果您愿意,可以使用三元。

var getChecklistUrl = function getChecklistUrl() {
    return dataLayerObject && dataLayerObject.config && dataLayerObject.config.checklist || null;
}

【讨论】:

    【解决方案4】:

    您总是可以利用强制转换来缩短它:

    if ( typeof dataLayerObject == 'object' &&
         dataLayerObject.config &&
         dataLayerObject.config.checklist &&
         typeof dataLayerObject.config.checklist == 'object') {
      // do something
    }
    

    如果你真的不需要检查清单是否真的是一个对象,你可以去掉最后一个typeof。第一个 typeof 是必需的,因为您的变量看起来可能是全局变量。

    【讨论】:

    • 这不会在所有情况下都有效,因为typeof null == 'object'.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-24
    • 1970-01-01
    • 1970-01-01
    • 2019-07-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多