检查对象是否已定义,最佳实践。

25

我从 ajax 请求中获得了以下 JSON 响应。

var json = {
    "response": {
        "freeOfChargeProduct": {  
        "description": "Product",  
        "orderQty": 5,
        "productName": "XYZ",
        "qty": 6,
        "details": {
            "price": 55.5, 
            "instock": "true",
            "focQuantity": 1
        }
    }, 
    "orderLineId": 4788,
    "totalOrderLinePrice": "741.36",
    "totalOrderPrice": "1,314.92",
    "totalQty": 17
};

JSON并不总是返回"freeOfChargeProduct"属性。所以,如果我想获取"freeOfChargeProduct"的价格,那么我必须执行以下操作:

var getFreeOfChargeProductPrice = function() { 
   var r = json.response;
   if (r && r.freeOfChargeProduct && r.freeOfChargeProduct.details) {
      return r.freeOfChargeProduct.details.price;         
   }
   return null;
};
没有问题。但是逐个检查对象中的每个属性非常令人困扰,因此我创建了一个函数来检查对象中的属性是否已定义。
var getValue = function (str, context) {
    var scope = context || window,
        properties = str.split('.'), i;
    for(i = 0; i < properties.length; i++) {
      if (!scope[properties[i]]) {                       
         return null;
      } 
      scope = scope[properties[i]];        
    }
    return scope;
};

var price = getValue('json.response.freeOfChargeProduct.details.price');
// Price is null if no such object exists.
现在来到我的问题:这是检查对象中是否存在属性的好方法还是坏方法?有更好的建议/方法吗? 编辑: 我不想使用 && 运算符。我很懒,正在寻找一种可重用的方法来检查是否定义了对象(或对象的属性)。 :) 谢谢!

1
我之前有一个类似的问题。你可能会对其中一些回答感兴趣。 - user113716
谢谢你的回答!我找到了一个答案(由kennebec发布),并给了它+1 :) - nekman
4个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
24

使用守卫模式:

if (json.response && json.response.freeOfChargeProduct && json.response.freeOfChargeProduct.details) {
    // you can safely access the price
}  

这就是守卫模式的工作原理。

if (a && a.b && a.b.c) { ... } else { ... }

第一次检查是 "属性a是否存在?"。如果不存在,则执行else分支。如果存在,则进行下一次检查,即 "对象a是否包含属性b?"。如果不包含,则执行else分支。如果包含,则进行最后一次检查:"对象a.b是否包含属性c?"。如果不包含,则执行else分支。只有在包含并且存在这些属性的情况下,才会执行if分支。

更新:为什么称之为“保护模式”?

var value = a && b;  
在此示例中,成员b(右操作数)由&&运算符进行保护。只有当成员 a (左操作数)为真(“值得”的意思)时,才会返回成员b。但是,如果成员 a 为假(“不值得”的意思),则它本身将被返回。 顺便说一句,如果成员返回以下值,则它们是虚假的:nullundefined0""falseNaN。在所有其他情况下,成员都是真实的。

10
你称它为“guard”,我称之为逻辑与(logical AND)......我以前从未听说过“guard operator”,但也许有人可以指引我一下资料来源? - Felix Kling
3
嗯,我会小心使用这种昵称。你的解释可能在JavaScript中“有效”,但在例如PHP中不一定适用。我会说它可以“像一个卫兵一样起作用”...无论如何,这可能是吹毛求疵,所以无妨!感谢提供链接! - Felix Kling
3
Crockford在他的书中告诉我们不要这样使用,同时强调在特定情况下,使用typeof和这些&&组合起来非常重要。请参阅《JavaScript权威指南》。 - KakambaWeb
5
注意:这不是“守卫/模式”,也不仅仅是AND运算符的正常行为。逻辑运算符(AND、OR等)的这种行为被称为短路求值,其根源可以追溯到C/C++和之前的一些语言。并非所有语言都表现出这种行为——例如VisualBasic(我讨厌它)不会短路:a && a.b && a.b.c如果'a'为null,则会抛出异常,因为VB评估所有子表达式,然后执行逻辑AND... - quetzalcoatl
@quetzalcoatl && 运算符是逻辑 AND 运算符。然而,它也可以被称为“guard”。这是一个非正式的名称。如果我决定使用这个名称,我可以这样做。根据 Crockford 的说法:“&& 运算符通常被称为逻辑与。它也可以被称为 guard。”我不明白你的观点是什么。 - Šime Vidas
显示剩余10条评论

21
if(x && typeof x.y != 'undefined') {
    ...
}

// or better
function isDefined(x) {
    var undefined;
    return x !== undefined;
}

if(x && isDefined(x.y)) {
    ...
}

这将适用于 JavaScript 中的任何数据类型,甚至是数字零。如果您要检查对象或字符串,则只需在 if 语句中使用 x && x.y,或者如果您已经知道 x 是一个对象,则使用if(x.y) ...


0
你可以像这样做:
try{
    var focp = json.response.freeOfChargeProduct
    var text = "You get " + focp.qty + " of " +
        focp.productName +
        " for only $" + (focp.qty-focp.details.focQuantity)*focp.details.price +
        ", You save $" + focp.details.focQuantity*focp.details.price;
    $("order_info").innerText = text;
} catch(e) {
    // woops, handle error...
}

如果提供的数据中存在字段,则会生成类似于此的消息:

您只需要支付$277.5,即可获得6个XYZ,您将节省$55.5。

如果数据不存在,则会进入catch块。如果您找不到处理错误的方法(也许可以为数据执行新的AJAX请求?),那么您总是可以尝试一下Try、Catch、Forget。


-2

这不是语法问题,而是设计模式问题。

问题A。 * 你有json服务器的控制权吗?

如果答案是否定的,我认为情况将完全取决于客户端。

请阅读以下内容: http://martinfowler.com/eaaDev/PresentationModel.html

由于服务器是数据源,在这种情况下它将提供模型。 该模式指定了一个附加工件:演示模型(PM)。在JavaScript中,我建议使用两个工件,一个用于转换器代码。

根据这个设计模式,PM负责将模型转换为PM,必要时再转换回来。在您的情况下,从PM到M的任何转换都不会发生。

这意味着js对象具有消化模型并翻译自身的方法或构造函数,借助于转换器(如下所示)。

这样做,您最终将得到一个像这样的PM:

var OrderlinePM = {
    "hasFreeOfCharge": false | true,
    "freeOfCharge" : {...}

    `enter code here`

    this.getFreeOfCharge = function() {
        ...
    }

    this.fromModel = function(jsonEntry, convertor) {
        //convert this with the convertor ;) to a for this specific view usable OrderlinePM
       // also inwith 
       ...
    }

    enter code here
    "orderLineId":0, 
    "totalOrderLinePrice":"741.36", 
    "totalOrderPrice":"1,314.92", 
    "totalQty":17
};

function mySpecialFunctionPMConvertor {
    this.fromModel = function() {
        ... //do strange stuff with the model and poulate a PM with it.
    }
}

好吧,我放弃在这个富文本编辑器中尝试格式化代码 :(

  • 您可以为不同任务分别拥有几个 PM,这些任务都依赖于同一模型对象。
  • 此外,这将使转换器对象可测试,可以在某些可以自动执行的东西中执行.... 嗯,好吧,也许是手动执行,但无论如何。

因此,繁琐的反射代码问题实际上并不是问题。但是,在 JavaScript 中,内聚性是一个问题。


首先感谢您抽出时间回答问题。我认为这个问题期望得到一个关于在对象中检查属性是否存在的最佳实践的答案。尽管您的回答非常详细,但并没有回答这个问题,也没有提供如何检查对象并查看特定属性是否存在的最佳实践。 - coderdark

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