JS常用方法原生实现

本文目录:

  • 1.手写reduce
  • 2.手写map
  • 3.手写filter
  • 4.手写防抖
  • 5.手写节流
  • 6.实现flat
  • 7.手写深拷贝
  • 8.手写Promise

1.手写reduce

reduce 接收两个参数:
第一个参数是在每一项上调用的函数
该函数接收 4 个参数:
1.前一个值 prev
2.当前值 cur
3.项的索引 index
4.数组对象 array
第二个可选参数是作为归并基础的初始值
reduce 方法返回一个最终的值

Array.prototype.myReduce = function (fn, initialValue) {
  console.log(this)
  let acc = initialValue || this[0];
  const startIndex = initialValue ? 0 : 1;
  for (let i = startIndex; i < this.length; i++) {
    acc = fn(acc, this[i], i, this);
  }
  return acc;
};

2.手写map

map 迭代方法接收两个参数:

  • 对每一项执行的函数
    • 该函数接收三个参数:
      • 数组项的值
      • 数组项的下标
      • 数组对象本身
  • 指定 this 的作用域对象

map 方法返回每次函数调用结果组成的数组。
代码表示:

arr.map(function(item, index, arr) {}, this);

代码实现:

Array.prototype.myMap = function (fn, context) {
  const array = this
  context = Object(context) || global   // 严格模式下
  const resultArr = []
  
  for (let i = 0; i < array.length; i++) {
    let item = array[i]
    result = fn.call(context, item, i, array)
    resultArr.push(result)
  }
  return resultArr
};

注: 严格模式下,context 为 null 或 undefined 时 Object(context) 返回空对象,不会被赋值为global

3.手写filter

filter 方法接收两个参数:

  • 对每一项执行的函数
    • 该函数接收三个参数:
      • 数组项的值
      • 数组项的下标
      • 数组对象本身
  • 指定 this 的作用域对象

filter 方法返回 执行结果为true的项组成的数组。
代码表示:

arr.filter(function(item, index, arr){}, context)

代码实现:

Array.prototype.fakeFilter = function fakeFilter(fn, context) {
  if (typeof fn !== "function") {
    throw new TypeError(`${fn} is not a function`);
  }
  
  let arr = this;
  let temp = [];

  for (let i = 0; i < arr.length; i++) {
    let result = fn.call(context, arr[i], i, arr);
    if (result) temp.push(arr[i]);
  }
  return temp;
};

4.手写防抖

函数防抖 是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。
通俗点讲就是=>你点的再快都没用(新的点击会清除上一次的点击效果),要特定时间之后才会触发
代码示例

let time2;
document.getElementById('防抖').onclick =
  function () {
      clearTimeout(time2);
      time2=setTimeout(function () {
      //do something***
      },2000);
  };

5.手写节流

函数节流 是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
通俗点讲就是=>点击就可以触发事件,但是特定时间内只会触发一次,(技能冷却)
代码示例

let bool=true;
document.getElementById('节流').onclick = function () {
   if(bool){
     //do something***
     bool=false;
     setTimeout(()=>{
       bool=true
     },2000)
   }
}

6.实现flat

flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
定义一个数组ary以及将ary转换为字符串str

let ary = [1, [2, [3, [4, 5]]], 6];
let str = JSON.stringify(ary);

直接调用flat方法实现多维数组的扁平化

let ary_flat = ary.flat(Infinity);

如果不用flat,第一种实现方法

ary = str.replace(/(\[\]))/g, '').split(',');

第二种实现方法

str = str.replace(/(\[\]))/g, '');
str = '[' + str + ']';
ary = JSON.parse(str);

第三种实现方法

let result = [];
let myFlat = function (ary) {
  for (let i = 0; i < ary.length; i++) {
    let item = ary[i];
    if (Array.isArray(ary[i])) {
      myFlat(item);
    } else {
      result.push(item);
    }
  }
  return result;
};

第四种实现方法

while (ary.some(Array.isArray)) {
  console.log('xxx')
  ary = [].concat(...ary);
}

7.手写深拷贝

我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度:是仅“深”拷贝第一层级的对象属性或数组元素,还是递归拷贝所有层级的对象属性和数组元素
怎么检验深拷贝成功
改变任意一个新对象/数组中的属性/元素, 都不改变原对象/数组
方法一:深拷贝的最简单直接的方法:
乞丐版: JSON.parse(JSON.stringify(object)),缺点诸多(会忽略undefined、symbol、函数;不能解决循环引用;不能处理正则、new Date())
方法二:手动写递归
基础版(面试够用): 浅拷贝+递归 (只考虑了普通的 object和 array两种数据类型)

var array = [
   { number: 1 },
   { number: 2 },
   { number: 3 }
];
function copy (obj) {
        var newobj = obj.constructor === Array ? [] : {};
        if(typeof obj !== 'object'){
            return;
        }
        for(var i in obj){
           newobj[i] = typeof obj[i] === 'object' ?
           copy(obj[i]) : obj[i];
        }
        return newobj
}
var copyArray = copy(array)
copyArray[0].number = 100;
console.log(array); //  [{number: 1}, { number: 2 }, { number: 3 }]
console.log(copyArray); // [{number: 100}, { number: 2 }, { number: 3 }]

上面这两个深拷贝的方法最大缺点是都会丢失函数Function、正则表达式等特殊类型。

8.手写Promise

简单版本,理解其原理

function myPromise(executor) {
  var _this = this;
  this.onFulfilled = []; //成功的回调
  this.onRejected = []; //失败的回调
  this.state = "PENDING"; //状态
  this.value = undefined; //成功结果
  this.reason = undefined; //失败原因
  function resolve(value) {
    if (_this.state === "PENDING") {
      _this.state = "FULFILLED";
      _this.value = value;
      _this.onFulfilled.forEach((fn) => fn(value));
    }
  }
  function reject(reason) {
    if (_this.state === "PENDING") {
      _this.state = "REJECTED";
      _this.reason = reason;
      _this.onRejected.forEach((fn) => fn(reason));
    }
  }
  try {
    executor(resolve, reject);
  } catch (e) {
    reject(e);
  }
}
// 定义链式调用的then方法
myPromise.prototype.then = function (onFullfilled, onRejected) {
  if (this.state === "FULFILLED") {
    typeof onFulfilled === "function" && onFulfilled(this.value);
  }
  if (this.state === "REJECTED") {
    typeof onRejected === "function" && onRejected(this.reason);
  }
  if (this.state === "PENDING") {
    typeof onFulfilled === "function" &&
      this.onFulfilled.push(onFulfilled);
    typeof onRejected === "function" && this.onRejected.push(onRejected);
  }
};
var myP = new myPromise((resolve, reject) => {
  console.log("执行");
  setTimeout(() => {
    reject(3);
  }, 1000);
});
myP.then(
  (res) => {
    console.log(res);
  },
  (err) => {
    console.log(err);
  }
);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容