minhuaF / blog

I will write my front-end story.
9 stars 1 forks source link

【JS基础】从源码角度认识indexOf与includes的区别 #14

Open minhuaF opened 3 years ago

minhuaF commented 3 years ago

今天review代码时看到了这个

if([VAR_1, VAR_2, VAR_3].indexOf(target) !== -1){}

想到了自己写的这个

if([VAR_1, VAR_2, VAR_3].includes(target)){}

都能实现同样的功能,那为什么会有不同的写法,它们又有什么区别呢? 抱着死记硬背不如了解下原理的原则,找了indexOfincludes的源码做了下对比,这里就不重新实现源码的,了解功能和区别就好了,所以直接从MDN上面把对应的代码搬到这里吧.

找了一遍,没有找到哪里能看javascript的源码,只能参考indexOfployfill

indexOf

使用方法

arr.indexOf(searchElement[, fromIndex])

实现

// indexOf polyfill
// Production steps of ECMA-262, Edition 5, 15.4.4.14
// Reference: http://es5.github.io/#x15.4.4.14
if (!Array.prototype.indexOf) {
  Array.prototype.indexOf = function(searchElement, fromIndex) {

    var k;

    // 1. Let O be the result of calling ToObject passing
    //    the this value as the argument.
    if (this == null) {
      throw new TypeError('"this" is null or not defined');
    }

    var O = Object(this);

    // 2. Let lenValue be the result of calling the Get
    //    internal method of O with the argument "length".
    // 3. Let len be ToUint32(lenValue).
    var len = O.length >>> 0;

    // 4. If len is 0, return -1.
    if (len === 0) {
      return -1;
    }

    // 5. If argument fromIndex was passed let n be
    //    ToInteger(fromIndex); else let n be 0.
    var n = +fromIndex || 0;

    if (Math.abs(n) === Infinity) {
      n = 0;
    }

    // 6. If n >= len, return -1.
    if (n >= len) {
      return -1;
    }

    // 7. If n >= 0, then Let k be n.
    // 8. Else, n<0, Let k be len - abs(n).
    //    If k is less than 0, then let k be 0.
    k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

    // 9. Repeat, while k < len
    while (k < len) {
      // a. Let Pk be ToString(k).
      //   This is implicit for LHS operands of the in operator
      // b. Let kPresent be the result of calling the
      //    HasProperty internal method of O with argument Pk.
      //   This step can be combined with c
      // c. If kPresent is true, then
      //    i.  Let elementK be the result of calling the Get
      //        internal method of O with the argument ToString(k).
      //   ii.  Let same be the result of applying the
      //        Strict Equality Comparison Algorithm to
      //        searchElement and elementK.
      //  iii.  If same is true, return k.
      if (k in O && O[k] === searchElement) {
        return k;
      }
      k++;
    }
    return -1;
  };
}

从代码中可以看到indexOf

  1. 返回匹配元素的位置,如果没有找到,返回-1;
  2. 匹配的规则是用 ===,全等比较隐藏的问题就是类型装换问题,典型的就是 (NaN === NaN) === false;所以使用Array.prototype.indeOf()是判断不了NaN是否存在的。
    if (k in O && O[k] === searchElement) {
    return k;
    }

includes

includes的实现也是参考polyfill

使用方法

arr.includes(valueToFind[, fromIndex])

实现

// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
  Object.defineProperty(Array.prototype, 'includes', {
    value: function(valueToFind, fromIndex) {

      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      // 1. Let O be ? ToObject(this value).
      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If len is 0, return false.
      if (len === 0) {
        return false;
      }

      // 4. Let n be ? ToInteger(fromIndex).
      //    (If fromIndex is undefined, this step produces the value 0.)
      var n = fromIndex | 0;

      // 5. If n ≥ 0, then
      //  a. Let k be n.
      // 6. Else n < 0,
      //  a. Let k be len + n.
      //  b. If k < 0, let k be 0.
      var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);

      function sameValueZero(x, y) {
        return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
      }

      // 7. Repeat, while k < len
      while (k < len) {
        // a. Let elementK be the result of ? Get(O, ! ToString(k)).
        // b. If SameValueZero(valueToFind, elementK) is true, return true.
        if (sameValueZero(o[k], valueToFind)) {
          return true;
        }
        // c. Increase k by 1.
        k++;
      }

      // 8. Return false
      return false;
    }
  });
}

从实现中得出includes

  1. 返回的是bool
  2. 判断也是使用 ===,但是有对NaN有特殊的处理
    function sameValueZero(x, y) {
    return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
    }

结论

  1. 如果想要知道元素(字符串)的位置,就使用indexOf
  2. 如果想要判断元素是否在数组(字符串)中,使用includes会比较方便;
  3. indexOf不能判断NaNincludes可以;
minhuaF commented 3 years ago

拙见,如果问题,请指教~