Interprétation du code source de soulignement: Comment écrire élégamment une méthode "rechercher un élément spécifié dans un tableau"

Digression

Permettez-moi d'abord de parler un peu hors sujet.

Depuis le début de la série d'articles d'interprétation soulignés le 16 mai, plus de 160 étoiles ont été reçues à ce jour. Je tiens à vous remercier pour votre soutien ici, et je continuerai à travailler dur pour partager les produits secs dans le code source. J'ai un message privé d'un ami disant que je n'ai pas vu la mise à jour depuis plusieurs jours. Veuillez me pardonner. Après tout, je considère cela comme l'un des projets de cette année et je dois généralement aller travailler. Je ne peux utiliser que mon temps libre. Les exigences de qualité de l'article sont relativement élevées: s'il s'agit d'un article à flux uniforme, le lecteur n'apprendra rien et il ne pourra pas passer le niveau. En fait, si vous êtes intéressé, vous devriez être en mesure de constater que les commentaires en texte intégral du code source underscore-1.8.3 ont été mis à jour (le nombre de lignes de commentaires a presque dépassé 1000).

Principale

Plus près de nous, dans le dernier chapitre, nous avons terminé la partie Méthode d'extension d'objet, et aujourd'hui nous allons commencer à interpréter la méthode d'extension de partie Array. En fait, le tableau en JavaScript est mon type préféré. Il peut simuler des structures de données telles que des piles et des files d'attente. Il peut également insérer des éléments (épissure) à volonté. Il est très flexible. Ceux qui ont fait du leetcode devraient avoir une compréhension approfondie de cela (ici aussi d'ailleurs. Téléchargez ma solution de problème leetcode Repo https://github.com/hanzichi/leetcode ).

Aujourd'hui, je veux parler de la façon de trouver des éléments dans un tableau, correspondant aux méthodes .findIndex, .findLastIndex, .indexOf, .lastIndexOf et _.sortIndex dans le trait de soulignement .

Attendez, est-ce un peu familier? Oui, la méthode indexOf (ES5) et la méthode findIndex (ES6) ont été déployées en JavaScript. Ce n'est pas introduit, vous pouvez apprendre par vous-même.

Examinons d'abord les fonctions .findIndex et .findLastIndex. Si vous comprenez la méthode Array.prototype.findIndex (), ce sera très simple. La fonction de .findIndex est de trouver le premier élément qui satisfait une certaine condition à partir d'un tableau, et .findLastIndex est de trouver le dernier (ou de rechercher dans l'ordre inverse).

Donnez un exemple simple:


var arr = [1, 3, 5, 2, 4, 6];

var isEven = function(num) {

  return !(num & 1);

};

var idx = _.findIndex(arr, isEven);

// => 3

En regardant directement le code source, les commentaires ont été écrits très clairement. Notez ici que la fonction de prédicat passe en fait les éléments du tableau dans ce paramètre et renvoie une valeur booléenne. Si elle renvoie vrai, cela signifie que cette condition est remplie, si elle est fausse, le contraire est vrai.


// Generator function to create the findIndex and findLastIndex functions

// dir === 1 => 从前往后找

// dir === -1 => 从后往前找

function createPredicateIndexFinder(dir) {

  // 经典闭包

  return function(array, predicate, context) {

    predicate = cb(predicate, context);

    var length = getLength(array);

    // 根据 dir 变量来确定数组遍历的起始位置

    var index = dir > 0 ? 0 : length - 1;

    for (; index >= 0 && index < length; index += dir) {

      // 找到第一个符合条件的元素

      // 并返回下标值

      if (predicate(array[index], index, array)) return index;

    }

    return -1;

  };

}

// Returns the first index on an array-like that passes a predicate test

// 从前往后找到数组中 `第一个满足条件` 的元素,并返回下标值

// 没找到返回 -1

// _.findIndex(array, predicate, [context])

_.findIndex = createPredicateIndexFinder(1);

// 从后往前找到数组中 `第一个满足条件` 的元素,并返回下标值

// 没找到返回 -1

// _.findLastIndex(array, predicate, [context])

_.findLastIndex = createPredicateIndexFinder(-1);

Ensuite, regardez la méthode _.sortIndex. Cette méthode est très simple dans son utilisation et sa mise en œuvre. Si vous insérez un élément dans un tableau ordonné afin que le tableau continue à être ordonné, quelle est la position d'insertion? C'est la fonction de cette méthode, ordonnée, utilisant évidemment la recherche binaire. Pas grand chose à dire, allez directement au code source.


// _.sortedIndex(list, value, [iteratee], [context])

_.sortedIndex = function(array, obj, iteratee, context) {

  // 注意 cb 方法

  // iteratee 为空 || 为 String 类型(key 值)时会返回不同方法

  iteratee = cb(iteratee, context, 1);

  // 经过迭代函数计算的值

  var value = iteratee(obj);

  var low = 0, high = getLength(array);

  while (low < high) {

    var mid = Math.floor((low + high) / 2);

    if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;

  }

  return low;

};

Enfin, nous parlons des méthodes .indexOf et .lastIndexOf.

ES5 introduit les méthodes indexOf et lastIndexOf, mais IE <9 ne la prend pas en charge. On vous demande d'écrire un Polyfill pendant l'interview. Que feriez-vous (vous pouvez considérer l'implémentation du trait de soulignement comme un Polyfill)? Comment satisfaire l'intervieweur? Tout d'abord, si vous écrivez séparément, c'est-à-dire que les deux méthodes sont écrites de manière relativement indépendante, il est évident que la quantité de code sera plus importante, car les fonctions des deux méthodes sont similaires, vous pouvez donc trouver un moyen d'appeler une méthode et de passer différentes parties en tant que paramètres pour réduire la quantité de code . Deuxièmement, si le tableau est déjà ordonné, peut-on utiliser un algorithme de recherche binaire plus rapide? Ce point sera un plus.

Implémentation du code source:


// Generator function to create the indexOf and lastIndexOf functions

  // _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);

  // _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

  function createIndexFinder(dir, predicateFind, sortedIndex) {

    // API 调用形式

    // _.indexOf(array, value, [isSorted])

    // _.indexOf(array, value, [fromIndex])

    // _.lastIndexOf(array, value, [fromIndex])

    return function(array, item, idx) {

      var i = 0, length = getLength(array);

      // 如果 idx 为 Number 类型

      // 则规定查找位置的起始点

      // 那么第三个参数不是 [isSorted]

      // 所以不能用二分查找优化了

      // 只能遍历查找

      if (typeof idx == 'number') {

        if (dir > 0) { // 正向查找

          // 重置查找的起始位置

          i = idx >= 0 ? idx : Math.max(idx + length, i);

        } else { // 反向查找

          // 如果是反向查找,重置 length 属性值

          length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;

        }

      } else if (sortedIndex && idx && length) {

        // 能用二分查找加速的条件

        // 有序 & idx !== 0 && length !== 0

        // 用 _.sortIndex 找到有序数组中 item 正好插入的位置

        idx = sortedIndex(array, item);

        // 如果正好插入的位置的值和 item 刚好相等

        // 说明该位置就是 item 第一次出现的位置

        // 返回下标

        // 否则即是没找到,返回 -1

        return array[idx] === item ? idx : -1;

      }

      // 特判,如果要查找的元素是 NaN 类型

      // 如果 item !== item

      // 那么 item => NaN

      if (item !== item) {

        idx = predicateFind(slice.call(array, i, length), _.isNaN);

        return idx >= 0 ? idx + i : -1;

      }

      // O(n) 遍历数组

      // 寻找和 item 相同的元素

      // 特判排除了 item 为 NaN 的情况

      // 可以放心地用 `===` 来判断是否相等了

      for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {

        if (array[idx] === item) return idx;

      }

      return -1;

    };

  }

  // Return the position of the first occurrence of an item in an array,

  // or -1 if the item is not included in the array.

  // If the array is large and already in sort order, pass `true`

  // for **isSorted** to use binary search.

  // _.indexOf(array, value, [isSorted])

  // 找到数组 array 中 value 第一次出现的位置

  // 并返回其下标值

  // 如果数组有序,则第三个参数可以传入 true

  // 这样算法效率会更高(二分查找)

  // [isSorted] 参数表示数组是否有序

  // 同时第三个参数也可以表示 [fromIndex] (见下面的 _.lastIndexOf)

  _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);

  // 和 _indexOf 相似

  // 反序查找

  // _.lastIndexOf(array, value, [fromIndex])

  // [fromIndex] 参数表示从倒数第几个开始往前找

  _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

Une chose à noter ici est que le troisième paramètre de la méthode .indexOf peut représenter [fromIndex] ou [isSorted], tandis que le troisième paramètre de .lastIndexOf ne peut représenter que [fromIndex], comme nous pouvons facilement le voir dans le code:


_.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);

_.lastIndexOf = createIndexFinder(-1, _.findLastIndex);

Je suis également perplexe sur ce point. Je ne sais pas à quoi sert cette restriction. Bienvenue pour en discuter ~

Enfin, les emplacements du code source des cinq méthodes impliquées dans cet article sont donnés https://github.com/hanzichi/underscore-analysis/blob/master/underscore-1.8.3.js/src/underscore-1.8.3.js#L613- L673

Je suppose que tu aimes

Origine blog.51cto.com/15080022/2588319
conseillé
Classement