2013-03-14 13 views
2

我一直在尋找一個代碼庫,不幸看到了很多類似的代碼:安全的方法來檢查可讀的深層物體?

if (module && module.foo && module.foo.bar && module.foo.bar.baz) 

我開始使用Google,看看是否有一個lib或東西,使這個容忍的;沒有找到,但我仍然確定某處存在)。我寫這個很快,但有點討厭它,因爲傳入根對象和一個字符串來分割'。'是很醜的;希望有更好的東西:

<!DOCTYPE html> 
<html> 
<head> 
    <title>safe tests</title> 
    <script type="text/javascript"> 
     function assert(condition, desc) { 
      var d = document, li = d.createElement("li"); 
      li.className = condition ? "pass" : "fail"; 
      li.appendChild(d.createTextNode(desc)); 
      d.getElementById('results').appendChild(li); 
     } 
    </script> 
    <style type="text/css"> 
     #results li.pass { color: green; } 
     #results li.fail{ color: red; } 
    </style> 
</head> 
<body> 
<ul id="results"></ul> 
<script type="text/javascript"> 
function safe(root, s) { 
    var split = s.split('.'), checked = root; 
    for (var i = 0, len = split.length; i < len; i++) { 
     if (checked[split[i]]) { 
      checked = checked[split[i]]; 
      continue; 
     } else { 
      return false; 
     } 
    } 
    return true; 
} 
var foo = { 
    bar: { 
     baz: { 
      qux: 'yippie!' 
     } 
    } 
} 
assert(safe(foo, 'bar'), "finds first sub object"); 
assert(safe(foo, 'bar.baz'), "finds first and then second sub object"); 
assert(safe(foo, 'bar.baz.qux'), "finds first, second, third, sub object"); 
assert(!safe(foo, 'bar.baz.qux.NOT'), "rejects if not defined (end)"); 
assert(!safe(foo, 'NOPE.baz.qux.NOT'), "rejects if not defined (front)"); 
assert(!safe(foo, 'bar.NO.baz'), "rejects if not defined (middle)"); 
</script> 
</body> 
</html> 

任何建議或庫已經處理這是精益?

+0

從我所看到的,你的摘要可能失敗,並: 變種富= { 條:{ 巴茲:{ qux:假 }} } 除此之外,它似乎做的工作。 @Slukehart的解決方案看起來不錯。 – eburgos 2013-03-14 01:02:00

+0

@eburgos如果包含false,那麼這是最後一個prop的好點; qux:錯誤...這很容易修復;但我的問題是一般的醜陋,我必須自己做(而不是使用現有的解決方案)。看起來這是一個應該解決的常見問題。 – Rob 2013-03-14 01:15:04

回答

2

我認爲方法:

if (module && module.foo && module.foo.bar && module.foo.bar.baz) 

仍然是最好的測試在一個對象的有效路徑。也就是說,我認爲你可以使用類似的東西來測試一個有效的對象路徑:

var safeObjectPath = function safeObjectPath(object, properties) { 
    var path = [], 
     root = object, 
     prop; 

    if (!root) { 
     // if the root object is null we immediately returns 
     return false; 
    } 

    if (typeof properties === 'string') { 
     // if a string such as 'foo.bar.baz' is passed, 
     // first we convert it into an array of property names 
     path = properties ? properties.split('.') : []; 
    } else { 
     if (Object.prototype.toString.call(properties) === '[object Array]') { 
      // if an array is passed, we don't need to do anything but 
      // to assign it to the internal array 
      path = properties; 
     } else { 
      if (properties) { 
       // if not a string or an array is passed, and the parameter 
       // is not null or undefined, we return with false 
       return false; 
      } 
     } 
    } 

    // if the path is valid or empty we return with true (because the 
    // root object is itself a valid path); otherwise false is returned. 
    while (prop = path.shift()) { 
     // Before it was used an if..else statement only, but it 
     // could generate an exception in case of inexistent 
     // object member. We can fix it using a try..catch 
     // statement. Thanks to @xecute for the contribution! 
     try { 
      if (prop in root) { 
       root = root[prop]; 
      } else { 
       return false; 
      } 
     } catch(e) { 
      return false; 
     } 
    } 

    return true; 
} 

該函數將接受一個陣列properties值。該參數在內部被轉換爲一個數組並對路徑進行測試。

屬性可以指定爲'foo.bar.baz'['foo','bar','baz']

爲了測試的有效路徑:

safeObjectPath(module) 
safeObjectPath(module, 'foo.bar.baz') 
safeObjectPath(module, [ 'foo', 'bar', 'baz' ]) 

請注意,所述第一形式(一個不properties參數)將返回true(如果傳遞根對象是有效的,當然),因爲這module是一個有效的路徑(根)。

可以用這個工作fiddle測試它。


我相信這將是不可能性考慮在一個遞歸和/或可綁定版本了。

編輯:我在我的文章發表在Coderwall擴展了這個答案與a more detailed analysis

這是performance test@xecute(再次感謝您的努力)構建的。

+0

http://stackoverflow.com/users/828197/ragnarokkr感謝您的建議。 >>仍然是測試對象中有效路徑的最佳選擇......你可以進一步探討爲什麼你覺得這是最好的方式嗎? – Rob 2013-03-14 17:04:57

+0

不客氣@Rob。這更多的是個人品味問題,但我認爲在需要驗證對象內部路徑效果的情況下,IF語句中的直接形式仍然是最好的模式,因爲立即清楚開發人員想要的是什麼在那一刻做(使用函數,即使非常清楚,無論如何都需要確定該函數將執行什麼操作),並且測試所需的計算時間將短於函數所需的時間(由於IF的短路評估以及處理的輕微超載)。 – Ragnarokkr 2013-03-14 18:34:19

+0

@Rob,抱歉的評論太長了..也就是說,除了特定和複雜的情況,我認爲當我們發現自己不得不處理如此複雜和嵌套的對象時,最好再考慮一下這個結構以及如何重構它。 – Ragnarokkr 2013-03-14 18:37:09

1

你有沒有遇到這個片段(as referenced here)?

/*decend through an object tree to a specified node, and return it. 
If node is unreachable, return undefined. This should also work with arrays in the tree.                        
Examples:                                            
    var test1 = {a:{b:{c:{d:1}}}};                                    
    console.log(objectDesend(test1, 'a', 'b', 'c', 'd'));                             
    var test2 = {a:{b:{c:1}}};  //will fail to reach d                                   
    console.log(objectDesend(test2, 'a', 'b', 'c', 'd')); 
*/ 

var objectDescend = function() { 
    var obj = arguments[0]; 
    var keys = arguments; 
    var cur = obj; 

    for (var i=1; i<keys.length; i++) {                                  
     var key = keys[i];                                     
     var cur = cur[key];                                    
     if(typeof(cur)=='undefined')                                  
      return cur;                                     
    }  

    return cur;                                       
}                                           

var test1 = {a:{b:{c:{d:1}}}};                                    
console.log(objectDescend(test1, 'a', 'b', 'c', 'd'));                             
var test2 = {a:{b:{c:1}}};                                    
console.log(objectDescend(test2, 'a', 'b', 'c', 'd')); 
+0

這與我所建議的基本相同(對於所有密集目的)+您需要多個可變參數;不是真的我在找什麼,但謝謝! – Rob 2013-03-14 01:10:13

+0

你的權利,也許是一種功能性的方法......我今晚會想到,它也一直在刺激我。 – Slukehart 2013-03-14 01:16:55

4
function safe(str, O){ 
    var seg= str.split('.'); 
    O= O || window; 
    while(O && seg.length) O= O[seg.shift()]; 
    return O; 
} 

與您的代碼一樣的想法,對於查找需要加載的代碼模塊非常有用。

+0

絕對喜歡你的代碼簡潔..謝謝! – Rob 2013-03-14 17:06:02

+0

+1。一般來說,你想先爲你的參數做Object。這就是說,這很好地轉化爲一個LoDash mixin:'_.mixin({'getDeep':function(O,str){var seg = str.split('。'); O = O || window ; while(O && seg.length)O = O [seg.shift()]; return O;}});' – 2014-10-20 20:02:53

0

相反的if,您可以使用靜默try ... catch

try { 
    var baz = module.foo.bar.baz; 
    ... 
} catch (e) { } 

這將退出第一線,如果modulefoobar丟失。

缺點是,這不會捕獲從塊內拋出的任何其他錯誤