如何动态检查全局作用域中嵌套对象的存在性?

3

我可以这样做来检查全局变量是否已定义:

if (window.globalVariableName) {
    // it's defined, now it's safe to work with it
}

现在我有一个嵌套层次结构,只有在每个嵌套对象都定义到叶子节点时才应该处理它,而这个嵌套层次结构可以是任何东西。例如,我可以有以下两个层次结构:
human.head.mouth.lips.upperLip.color

building.firstFloor.hallway.lastRoom.chair.density

我想检查所有嵌套的存在性,直到叶值,并且只有在包括叶子的整个层次结构被定义时才执行某些操作。换句话说,我希望能够动态创建这个静态代码:

if (window.human.head.mouth.lips.upperLip.color) {
   // now I know human is defined, and head, and mouth, and ... and up to color
}

我尝试了这段代码,但它不起作用:
function checkThenRun(targetToWaitFor, callback) {
    if (typeof callback !== "function") {
        console.error('Callback for checkThenRun should be a function');
        return;
    }
    if (targetToWaitFor.indexOf('.') > -1) {
        targetToWaitFor.split('.').reduce(function (anchor, recursion) {
            if (anchor) {
                globalVariableForReducingInWaitAndRun = window[anchor];
                if (globalVariableForReducingInWaitAndRun) {
                    globalVariableForReducingInWaitAndRun = globalVariableForReducingInWaitAndRun[recursion];
                }
            }
            else {
                globalVariableForReducingInWaitAndRun = globalVariableForReducingInWaitAndRun[recursion];
            }
        });
        if (!globalVariableForReducingInWaitAndRun) {
            console.warn('checkThenRun waiting for ' + targetToWaitFor + '...');
            setTimeout(function () {
                checkThenRun(targetToWaitFor, callback);
            }, 500);
            return;
        }
    }
    if (typeof window[targetToWaitFor] === "undefined") {
        console.warn('checkThenRun waiting for ' + targetToWaitFor + '...');
        setTimeout(function () {
            checkThenRun(targetToWaitFor, callback);
        }, 50)
        return;
    }
    callback();
}

这是用法示例:

checkAndRun('human.head.mouth.lips.upperLip.color', function() {
    // now I know this hierarchy is created, I can change color for example
});
更新:

我正在开发一个基于混合架构的系统。一个网页从许多不同的来源填充。我不能也没有访问那些外部来源的权限。我不能要求它们为我提供API,因为它们并不存在。这是另一个系统,它通过异步操作向页面注入一些东西,因此我不知道该操作何时完成。但我只能在那之后开始我的操作。这就是为什么我需要检查整个层次结构是否存在的原因。


1
为什么要等待?你想看看叶子是否突然从某个地方出现吗? - mplungjan
我猜另一个系统在全局空间中创建了这个层次结构。也许是另一个脚本。 - Saeed Neamati
这段代码存在一些问题。reduce使用不正确,你永远不能在其中返回任何内容,因此anchor将始终是undefinedglobalVariableForReducingInWaitAndRun从未被声明(使用letvar),这是有意为之吗?我认为它有点过于复杂,可以大大简化。 - blex
虽然这可能是一个有效的问题,但我必须在这里提出:你试图解决的问题应该已经从你的外部来源得到解决。也就是说,为什么应该定义 human,而不是 head?这是糟糕的设计。 - malifa
我认为 https://stackoverflow.com/questions/16142957/undefined-error-when-checking-undefined-in-an-if-statement 答案1是一个很好的解决方案。 - Kuo-hsuan Hsu
3个回答

2

看起来你需要等待某个(外部)脚本将内容添加到全局范围。通常,有方法可以避免这种情况。大多数库都允许你定义回调,当某些(异步)操作完成时调用。你应该真正查看你正在尝试使用的脚本/库的文档。如果绝对没有其他方法,你可以使用以下方法等待变量成为真值:

function waitUntilTruthy(cb) {
  return new Promise(resolve => {
    const interval = setInterval(() => {
      if (cb()) {
        clearInterval(interval);
        resolve();
      }
    }, 50);
  });
}

waitUntilTruthy(() => window?.human?.head?.mouth?.lips?.upperLip?.color).then(() => {
  // variable is defined 
});

请注意,可选链语法(a?.b?.c)将在 ECMAScript 2020 中出现,可能还不能在您的目标运行时(即浏览器和/或 Node.js)中使用。确保检查兼容性并考虑使用Babel或TypeScript 3.7+。请参阅如何避免“无法读取未定义属性”错误?,了解可选链的替代方案。

1
您的代码可以大大简化:

function checkThenRun(path, callback) {
  if (typeof callback !== "function") {
    console.error('Callback for checkThenRun should be a function');
    return;
  }
  if (getNestedProp(window, path)) {
    callback();
  } else {
    console.warn('checkThenRun waiting for ' + path + '...');
    setTimeout(function() {
      checkThenRun(path, callback);
    }, 500);
  }
}

function getNestedProp(obj, path) {
  const parts = path.split('.');
  let current = obj;
  for (let i = 0; i < parts.length; i++) {
    if (typeof current === 'object' && current !== null) {
      current = current[parts[i]];
    } else {
      return undefined;
    }
  }
  return current;
}

// Usage
checkThenRun('hello.world', () => { console.log('OK'); });

// For the demo
setTimeout(() => {window.hello = {world: 42}; }, 2500);


0

我知道在编程风格上,对于那些从程序逻辑的角度来看并不是错误的事情使用try/catch是可疑的... 但是,在这种情况下,其他替代方案似乎更加不清晰,因此:

try {
    temp=human.head.mouth.lips.upperLip.color;
}
catch(err) {
    temp=false;
}
if(temp){
    do_stuff(temp);
}

但不包括这个(它会隐藏函数中的错误):
try {
    temp=human.head.mouth.lips.upperLip.color;
    do_stuff(temp);
}
catch(err) {
}

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