2

我正在查看代码库,不幸的是看到了很多代码,例如:

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

我开始在谷歌上搜索,看看是否有一个库或其他东西可以让这种情况变得可以忍受;没有找到,但我仍然确定某处存在)。我写得很快,但有点讨厌它,因为传入根对象和一个字符串在'。'上分割很丑;希望有更好的东西:

<!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>

任何已经处理这个问题的建议或库是精简的?

4

4 回答 4

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

与您的代码相同的想法,对于查找需要加载的代码模块最有用。

于 2013-03-14T03:18:12.570 回答
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是一个有效的路径(根)。

可以用这个工作小提琴对其进行测试。


我相信也可以考虑递归和/或可绑定版本。

编辑:我在 Coderwall 上发表的文章中通过更详细的分析扩展了这个答案。

这是@xecute构建的性能测试(再次感谢您的努力)。

于 2013-03-14T02:01:02.837 回答
1

您是否遇到过这个片段(如此处所引用的)?

/*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'));
于 2013-03-14T00:57:00.720 回答
0

代替if,您可以使用静默try ... catch

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

如果或缺失module,这将在第一行退出。foobar

缺点是这不会捕获从块内抛出的任何其他错误。

于 2015-08-13T10:03:44.407 回答