Javascript检查对象属性是否存在,即使对象未定义

5

我想检查一个对象是否存在,并且是否有属性。目前我得到了一个“myObject未定义”的错误,导致检查停止。

当myObject可能不存在时,如何使以下代码仍然正常工作?

if (myObject.myProperty) {
  ...
} else {
  ...
}

我试图检查一个对象/变量是否存在,但是出现了错误:

if (foo) { console.log('hello'); }会报错Uncaught ReferenceError: foo is not defined。这里有一个jsfiddle http://jsfiddle.net/cfUss/

4个回答

9
您可以使用“短路”&&运算符:
if (myObject && myObject.myProperty) { 
    ...
}

如果myObject是“falsey”(例如未定义),那么&&运算符将不会尝试计算右侧表达式,从而避免了引用不存在对象属性的尝试。
变量myObject必须已经被声明,上面的测试是为了确定它是否已被赋予定义的值。

好的,那就是我一直在尝试的,但还是失败了...我会粘贴一份代码副本。 - Don P
如果 (foo) { ... } 给出错误 Uncaught ReferenceError:foo未定义 - Don P
看起来这只在myObject已经声明但是为falsey时才能运行 ...所以我猜只有当我执行了“var myObject;”或“var myObject = null;”时才能这样做。 - Don P
1
@DonnyP 是的,变量实际上必须先存在。当然,您应该始终定义 所有 变量。唯一的解决方法是 if (typeof myObject !== "undefined" && ...), 因为 typeof 运算符无法抛出 foo 未定义 错误。 - Alnitak

6
您可以使用 ?. 可选链操作符使内容更加简洁:
if (myObject?.myProperty) { ... }

这相当于更冗长的一个比较

if (myObject && myObject.myProperty) { ... }

它非常方便,特别是对于深度嵌套的对象。

它目前(2019年9月)是一个ECMAScript stage 3 proposal,但是事情正在朝着这个功能正式可用的方向发展。目前,您可以通过相应的 Babel 插件开始使用它:https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining


1

尝试:

if(myObject && myObject.myProperty){ ... }

如果myObject存在并且还有myproperty,则此代码进入if块的主体。如果由于某些原因myObject不存在,则&&会短路并不评估myObject.myProperty

就像上面一样,如果你用 var 声明变量,你应该没问题。 - Jesan Fafon

1

我很惊讶在基本的JavaScript接口中没有找到这个有用的功能。

以下是我经常在项目中使用的辅助函数。它检查最终链元素的值是否可达并且没有错误出现"无法获取未定义的..."。使用方式如下:

getChained(this, "this.path.to.some.object.someFunction().result");

/**
 * Deconstructs a chain, checking if every other element is undefined. 
 * You can use arrays and functions in chain and even pass parameters
 * inside them.
 *
 * The only limitation is no string values with dots are allowed.
 *
 * @param {Object} chainScope Starting object, the chain scope.
 *                            Guaranteed not to be be undefined.
 *                            Required parameter.
 * @param {String} chain A chain from code without first object 
 *                       and without first dot.
 *                       Required parameter.
 * @param {Array<Array<Object||String||Number||Boolean||undefined>>}
 *        functionsParameters Parameters for functions in chain.
 *                            Object in the array correspond the order
 *                            of functions in chain. Every object
 *                            contains parameters for its function.
 *                            Object syntax forces us to create parameter 
 *                            names even if you only have its value.
 * @returns {Object||String||Number||Boolean||undefined} Final chain element value or undefined, if any other chain element returned undefined.
 */
getChained: function (
  chainScope,
  chain,
  functionsParameters) {

  var parts;
  var part;
  var partIndex;
  var target = undefined;
  var functionIndex = 0;

  if (
    chainScope === undefined ||
    chainScope === null) {

    return target;
  }

  target = chainScope; // The starting scope of a chain.

  parts = getParts();

  // Relay chain substituting calculated parts with values
  // for function calls and arrays:
  for (
    partIndex = 0;
    partIndex < parts.length;
    partIndex++) {

    if (target === undefined) {
      // Chain element is undefined and so is the chain itself:
      return undefined;
    }

    part = parts[partIndex];

    if (
      part.indexOf("(") >
      part.indexOf("\"") &&
      part.indexOf("(") >
      part.indexOf("\'")) {

      // It's a function:

      target = getFunctionValue();
      functionIndex++;
      continue;
    }

    if (
      part.indexOf("[") >
      part.indexOf("\"") &&
      part.indexOf("]") >
      part.indexOf("\'")) {

      // It's an array's element:

      target = getArrayValue();
      continue;
    }

    if (
      typeof part === "string" &&
      target !== null &&
      target !== undefined) {

      // It's an object:

      target = target[part];
      continue;
    }
  }

  return target;

  /**
   * Splits string. Separators are dots outside the brackets.
   * No splitting for dots inside the brackets.
   */
  function getParts() {

    var SEPARATOR = ".";
    var OPEN_CHARS = [
      "(",
      "[",
      "\"",
      "\'"
    ];
    var CLOSE_CHARS = [
      ")",
      "]",
      "\"",
      "\'"
    ];
    var SUB_SEPARATOR_OPEN = "[";
    var SUB_SEPARATOR_CLOSE = "]";

    return(
      splitBySubSeparator(
        splitBySeparator(
          chain)));

    /**
     * Split by chain root separator.
     * No splitting between opening and closing characters.
     *
     * @param {String} chainString Chain to analyse characters.
     * @returns {Array<String>} Chain elements splitted.
     */
    function splitBySeparator(chainString) {
      var parts = [
      ];
      var opened = 0;
      var char1;
      var chainIndex;
      var extract;
      var cutFromIndex = 0;
      var chainArray;

      // String to array and attach the ending dot 
      // to be able to split using common rule:
      chainArray =
        (chainString + ".").
        split("");

      for (
        chainIndex = 0;
        chainIndex < chainArray.length;
        chainIndex++) {

        char1 = chainArray[chainIndex];

        if (OPEN_CHARS.indexOf(char1) > 0) {
          // It's an opening bracket:
          opened++;
          continue;
        }

        if (CLOSE_CHARS.indexOf(char1) > 0) {
          // It's a closing bracket:
          opened--;
          continue;
        }

        if (opened === 0) {
          // It's character outside the brackets:
          if (char1 === SEPARATOR) {

            // It's a dot - everything before it is an element:

            extract =
              chainArray.slice(
                cutFromIndex,
                chainIndex). // Cut an element.
              join(""); // Array to String.

            parts.push(
              extract);

            cutFromIndex = chainIndex + 1; // Shift to escape a dot.
          } else {
            // It's an ordinary character:
            continue;
          }
        }
      }

      return parts;
    }

    /**
     * Splits by root subobject or array elements calls.
     * Subcalls are searched inside the splitted chain elements.
     * (now separator is "[" instead of ".").
     * Can split several consequently called subobjects
     * without a need to deconstruct enclosures.
     * Second iteration finds array elements and object subcalls
     * inside resulting elements (now separator is "[" instead of "."):
     */
    function splitBySubSeparator(parts) {
      var newParts = [
      ];
      var opened = 0;
      var char1;
      var partIndex;
      var chainIndex;
      var chainArray;

      for (
        partIndex = 0;
        partIndex < parts.length;
        partIndex++) {

        var part = parts[partIndex];

        chainArray = part.split("");

        for (
          chainIndex = 0;
          chainIndex < chainArray.length;
          chainIndex++) {

          char1 = chainArray[chainIndex];

          if (
            opened === 0 &&
            char1 === SUB_SEPARATOR_OPEN) {

            // Start of subcall for an array element or object:
            part =
              part.substr(0, chainIndex) +
              SEPARATOR +
              part.substr(chainIndex + 1);

            opened++;
          }

          if (
            opened > 0 &&
            char1 === SUB_SEPARATOR_CLOSE) {

            // End of subcall for an array element or object:
            part =
              part.substr(0, chainIndex) +
              "" +
              part.substr(chainIndex + 1);

            opened--;
          }
        }

        // Split changed element by separators again and 
        // relay into a cumulative array:
        newParts =
          newParts.concat(
            splitBySeparator(part));
      }

      return newParts;
    }
  }

  /**
   * Creates and returns method call result. Puts required
   * parameters into method.
   *
   * @returns {Object||String||Number||Boolean||undefined} Method execution result.
   */
  function getFunctionValue() {
    var value;
    var name;

    name =
      part.
      split("(")[0];

    if (functionsParameters) {
      value =
        target[name].
        apply(
          target,
          functionsParameters[
            functionIndex
          ]);
    } else {
      value =
        target[name].
        apply(
          target);
    }

    return value;
  }

  /**
   * Returns array element.
   *
   * @returns {Object||String||Number||Boolean||undefined} Value of array element.
   */
  function getArrayValue() {
    var value;
    var arrayName;
    var itemName;

    arrayName =
      part.
      split("[")[0];

    itemName =
      (part.
        split("[")[1].
        split("]")[0]).
      split("\'").
      join("").
      split("\"").
      join("");

    if (target[arrayName]) {
      value =
        target[arrayName][itemName];
    }

    return value;
  }
}

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