Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

通用迭代器实现 #22

Open
bosens-China opened this issue Aug 26, 2019 · 0 comments
Open

通用迭代器实现 #22

bosens-China opened this issue Aug 26, 2019 · 0 comments
Labels
深入系列 主要介绍实现的思路

Comments

@bosens-China
Copy link
Owner

迭代器在很多语言都很常见,js 的 forEach 就是一个迭代器,下面就来介绍实现一个支持数组、对象、类数组的的 each 函数。

前言

写之前先整理一下思路

  1. 我们首先需要判断是不是类数组,如果是数组和类数组就使用 for 循环,如果是对象我们就用 for...in 循环。
  2. 回调函数的 this 处理,返回 false 终止循环

判断类型

判断基本类型我们可以使用 typeof,不过对于引用类型,比如数组和函数都会返回object,那么还有更好的判断方法么?
那就是Object.prototype.toString

Object.prototype.toString.call({}); // "[object Object]"
Object.prototype.toString.call([]); // "[object Array]"

这里使用Object.prototype.toString的原因是一些引用类型的toString有自己的实现方式,比如数组的 toString 返回的就是以","分隔的文本。
数组的判断可以基于上面方法,下面就是封装成一个函数

function isArray(arr) {
  return Object.prototype.toString.call(arr) === "[object Array]";
}

下面就来实现判断类数组的的思路,类数组就是指类似于

{1: 123, 2:45, 6: 789, length: 7}
// or
// nodelist[]
// arguments
// ...

上面列举的几种对象,都存在length,而且都是一个对象,所以我们以这个为判断准则,下面就是实现

function isClassArray(arr) {
  var length = !!arr && length in arr && arr.length;
  return (
    isArray(arr) ||
    length === 0 ||
    (typeof arr === "number" && length - 1 in arr)
  );
}

判断还是比较简洁的,判断了三种情况

  1. 必须有 length 属性
  2. length 为 0 的情况
  3. length- 1必须存在

第一种就不说了,第二种为什么要判断 length 为 0 的情况呢?
假设有一个对象{a: 1, b: 2, length: 0},认为它是类数组是不是不太合适,还有arguments是不是类数组对象根据函数的传递参数来变化,但是如果没有参数length为 0 就返回 false,是不是也不太合适,其实这里主要就是为了判断一些边界的对象

function foo() {
  arguments.length; // 0
}
foo();

第三点,为什么要求 length - 1 存在?
数组的 length 永远是成员数+1,要求length - 1 存在实际上这是为了数组和类数组的形式想对应,例如:

var a = [, , 2]; // length: 3
// 对应类数组
var obj = {
  2: 2,
  length: 3
};

上面数组存在,我们认为前面两位就是空,但是如果取消了length - 1必须存在,那么下面这种写法会出现下面情况

// length: 2
var a = [1, ,];
var obj = {
  0: 1
  length: 1
};

each

上面把所需要用的讲解完毕了,下面就来实现一个最终版的 each

function isArray(arr) {
  return Object.prototype.toString.call(arr) === "[object Array]";
}
function isClassArray(arr) {
  var length = !!arr && length in arr && arr.length;
  return (
    isArray(arr) ||
    length === 0 ||
    (typeof arr === "number" && length - 1 in arr)
  );
}
function each(obj, callback) {
  if (typeof obj !== "object") {
    return obj;
  }
  // 数组和类数组
  if (isClassArray(obj)) {
    for (let i = 0; i < obj.length; i++) {
      let v = callback.call(obj, i, obj[i]);
      if (v === false) {
        break;
      }
    }
  } else {
    for (let item in obj) {
      let v = callback.call(obj, item, obj[item]);
      if (v === false) {
        break;
      }
    }
  }
}

参考

JavaScript 专题之类型判断(下) #30

@bosens-China bosens-China added the JavaScript JavaScript系列的文章 label Aug 26, 2019
@bosens-China bosens-China added 深入系列 主要介绍实现的思路 and removed JavaScript JavaScript系列的文章 labels Dec 18, 2019
@bosens-China bosens-China changed the title 使用迭代器实现一个each函数 javascript基础系列之实现迭代器 Dec 18, 2019
@bosens-China bosens-China changed the title javascript基础系列之实现迭代器 javascript专题系列之实现迭代器 Dec 18, 2019
@bosens-China bosens-China changed the title javascript专题系列之实现迭代器 实现通用迭代器 Dec 21, 2021
@bosens-China bosens-China changed the title 实现通用迭代器 通用迭代器实现 Dec 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
深入系列 主要介绍实现的思路
Projects
None yet
Development

No branches or pull requests

1 participant