手写JavaScript中的数组方法(上)

简介: 手写JavaScript中的数组方法(上)

前言


为了巩固提高javascript的水平,我决定手写一些JavaScript中常见的方法,来达到我们熟悉js的目的。


手写深拷贝 ⛹⛹


我们知道,深拷贝其实就是在堆栈中新开辟一个空间,来克隆一个一摸一样的对象。

我们一般实现深拷贝会使用一下方法:

JSON.stringify(JSON.parse(obj))

通过这种方式我们会开辟一个空间,创建一个和obj一摸一样的对象。

那么我们如何手写一个深拷配的方法呢?

// 手写深拷贝
function deepClone(origin, target) {
  // 首先创建一个变量默认为传入的target对象或者为空对象
  var tar = target || {};
  var toStr = Object.prototype.toString;
  var arrayType = "[object Array]";
  for (const k in origin) {
    //   判断origin带有的属性k是继承来的还是创建的
    if (origin.hasOwnProperty(k)) {
      // 判断当前属性是对象和数组之一吗
      if (typeof origin[k] == "object" &&  origin[k] != null) {
        //  具体判断该属性是数组还是对象
        tar[k] = toStr.call(origin[k]) == arrayType ? [] : {};
        // 进行递归调用,将里面嵌套的对象或者数组也进行拷贝
        deepClone(origin[k], tar[k]);
      } else {
        tar[k] = origin[k];
      }
    }
  }
  return tar;
}

我来给大家讲解一些我的实现思路:

  1. 首先这个函数传入两个参数,一个要被克隆的原始对象(origin),一个克隆之后的对象(target)
  2. 然后在这个函数中创建变量tar保存target,或者创建一个空对象
  3. 接着使用for in 方法对传入的origin对象进行遍历,首先判断origin上的属性k是继承来的还是自己创建的。
  4. 然后就对传入的对象的属性进行判断,首先使用type of进行简单的一个过滤判断(因为typeof只能粗略的判断一下基本本数据类型,不能更细致的区分),这里还要注意一下因为null类型也会配判断为object类型,所以我们还要排除一下null类型。
  5. 然后我们就要区分是对象还是数组了,在这里就需要考察对JavaScript的掌握程度了,我们知道因为基本类型中的object类型下面又可以细分为function,array,object,null等类型,要想具体的将他们给区分开就需要使用Object.prototype.toString中的call方法。

Object.prototype.toString.call(null); // "[object Null]" Object.prototype.toString.call(undefined); // "[object Undefined]" Object.prototype.toString.call(“abc”);// "[object String]" Object.prototype.toString.call(123);// "[object Number]" Object.prototype.toString.call(true);// "[object Boolean]"

这个可以清楚的分清楚所属的类型

  1. 所以我们可以通过这行代码来实现针对属性创建一个对象或者数组

tar[k] = toStr.call(origin[k]) == arrayType ? [] : {};

  1. 然后通过递归调用传入origin[k],tar[k],因为此时我们已经可以判断出来origin[k]是对象还是数组了,所以我们传入originp[k]就相当于是最初传入origin一样,传入tar[k],就像是传入target一样。
  2. 然后我们就要给递归一个出口:如果origin中的这个属性不是对象或者数组,那么我们就可以直接进行赋值操作,将origin[k]赋值给tar[k]。
  3. 最后我们将tar给return出去就可以,这样就实现了深拷贝。


手写forEach🛰🛰


废话不多说,先上源代码:

// 手写ForEach方法
Array.prototype.myForEach = function (cb) {
  //   这里的this指的是调用这个方法的数组
  var _arr = this;
  var len = _arr.length;
  //   这个obj2代表的是myForEach的第二个参数,这个参数可以改变this指向
  var arr2 = arguments[1] || window;
  //   根据数组的长度来决定cb这个函数执行的次数
  for (var i = 0; i < len; i++) {
    // apply可以更改this的指向
    cb.apply(arr2, [_arr[i], i, _arr]);
  }
};

思路:

首先我们要给Array.prototype绑定一个myForeEach方法,这个函数接收一个参数是一个回调函数, 然后我们在函数内部声明一个变量用来保存this,首先我们要在这里明白的是这里的this指向调用他的数组,然后这个arr2保存的是可能会传递的二个参数,这个参数可以更改this的指向。然后我们就可以实现遍历,其实本质就是一个for循环,然后根据数组的长度来调用回调函数。其中apply方法可以更改this的指向,然后第二个参数传递一个数组,里面的参数就是要传入回调函数的参数。

实例数据:

let arr = [
  {
    name: "张三",
    age: 21,
  },
  {
    name: "李四",
    age: 24,
  },
  {
    name: "王五",
    age: 31,
  },
];


实际使用


arr.myForEach(function (item, index, arr) {
  // console.log(this);
   console.log(item);
});

image.png


手写map方法 🐤🐤


先上源代码:

// 手写map方法
Array.prototype.myMap = function (cb) {
  var _arr = this;
  var len = _arr.length;
  var arr2 = arguments[1] || window;
  var newArr = [];
  for (var i = 0; i < len; i++) {
    // 因为map要返回一个新的数组,所以里面如果有对象这种引用类型,需要进行深拷贝
    var _item = deepClone(_arr[i]);
    //  cb调用apply方法需要在cb中进行return,这也是map方法所需要做的
    newArr.push(cb.apply(arr2, [_item, i, _arr]));
  }
  return newArr;
};

注意这个方法前三行代码和forEach是相同的不再赘述,我们主要说不同的地方,首先我们还是使用for循环但是注意这里我们使用了前面写好的深拷贝函数deepClone克隆数组中的元素,然后创建一个新的空数组,将回调函数中return的元素push到newArr中,最后再将newArr给return出去,这样就完成了myMap方法


实际使用:


let newArr = arr.myMap(function (item, index, arr) {
   item.age += 100;
   return item;
 });
console.log(newArr);

这里的arr和示例数据相同,我们可以查看一下返回值:

image.png


手写filter方法 🥇🥇


我们知道过滤在我们实习的很多项目中都会使用到,可以用来筛选有用的数据,所以我们这次就手写一个

废话不多说,先上代码:

// 手写filter方法
Array.prototype.myFilter = function (cb) {
  var _arr = this;
  var len = _arr.length;
  var arr2 = arguments[1] || window;
  var newArr = [];
  var _item;
  for (var i = 0; i < len; i++) {
    _item = deepClone(_arr[i]);
    cb.apply(arr2, [_item, i, _arr]) ? newArr.push(_item) : "";
  }
  return newArr;
};

思路:

这个代码实现上面其实和之前map的实现方式有一点像,但略有不同,首先相同的是我们要对数组中的元素进行深拷贝,因为涉及对象这个引用类型,为了防止修改原对象就进行深拷贝,我们知道filter方法返回值其实是符合条件的值,所以可以进行判断如果这个值满足要求,那就将他push到newArr中。


实际使用:


let newArr2 = arr.myFilter(function (item, index, arr) {
  return item.age > 25;
});
 console.log(newArr2);

image.png


总结 🎃🎃


以上就是暂时手写出来的数组方法,后续还会继续更新手写数组方法的内容,通过手写方法可以是我们更加熟悉JavaScript的语法,提升js的基础

相关文章
|
21天前
|
前端开发 JavaScript 算法
使用 JavaScript 数组方法实现排序与去重
【10月更文挑战第21天】通过灵活运用 `sort()` 方法和 `filter()` 方法,我们可以方便地实现数组的排序和去重。同时,深入理解排序和去重的原理,以及根据实际需求进行适当的优化,能够更好地应对不同的情况。可以通过实际的项目实践来进一步掌握这些技巧,并探索更多的应用可能性。
96 59
|
21天前
|
JavaScript 前端开发
JavaScript 数组方法汇总
【10月更文挑战第21天】这些是 JavaScript 数组中一些常见的方法,它们为我们处理数组提供了强大的工具,使我们能够更加方便快捷地操作数组。你可以根据具体的需求选择合适的方法来实现相应的功能。同时,还可以通过组合使用这些方法来实现更复杂的数组操作。还可以进一步深入研究和探索其他数组方法,以发掘更多的应用场景和潜力。
86 59
|
6天前
|
缓存 JavaScript 前端开发
介绍一下 JavaScript 中数组方法的常见优化技巧
通过合理运用这些优化技巧,可以提高 JavaScript 中数组方法的执行效率,提升代码的整体性能。在实际开发中,需要根据具体的业务场景和数据特点选择合适的优化方法。
17 6
|
6月前
|
JavaScript 前端开发
JavaScript 数组方法概览
【5月更文挑战第11天】JavaScript 数组方法概览:push() 添加元素至末尾;pop() 删除并返回最后一个元素;shift() 删除并返回第一个元素;unshift() 向开头添加元素;slice() 返回指定范围的浅拷贝;splice() 删除/替换/添加元素,改变原数组;concat() 合并数组;join() 转换为字符串;reverse() 颠倒顺序;sort() 排序;map() 应用函数并创建新数组;filter() 过滤元素;reduce() 累加元素为单一值。
35 1
|
2月前
|
存储 缓存 JavaScript
JavaScript 中数组方法的常见优化技巧
JavaScript 中数组方法的常见优化技巧
|
5月前
|
Web App开发 JavaScript iOS开发
技术笔记:js数组定义和方法(包含ES5新增数组方法)
技术笔记:js数组定义和方法(包含ES5新增数组方法)
|
6月前
|
JavaScript 前端开发
JavaScript 的数组方法 map()、filter() 和 reduce() 提供了函数式编程处理元素的方式
【5月更文挑战第11天】JavaScript 的数组方法 map()、filter() 和 reduce() 提供了函数式编程处理元素的方式。map() 用于创建新数组,其中元素是原数组元素经过指定函数转换后的结果;filter() 则筛选出通过特定条件的元素生成新数组;reduce() 将数组元素累计为单一值。这三个方法使代码更简洁易读,例如:map() 可用于数组元素乘以 2,filter() 用于选取偶数,reduce() 计算数组元素之和。
46 2
|
6月前
|
存储 JavaScript 前端开发
JavaScript 数组方法详解
JavaScript 数组方法详解
46 2
|
6月前
|
JavaScript 前端开发
JavaScript 中的数组方法有哪些?
JavaScript 中的数组方法有哪些?
51 2
|
JavaScript 前端开发
手写JavaScript数组方法(下)
手写JavaScript数组方法(下)
42 0
手写JavaScript数组方法(下)