js常见手写函数(1)

最近整理学习了一些js的手写实现,本着分享和复习的心态,给大家分析并分享以下,然后代码也都上传GitHub了 地址:github.com/dizao006/--... 话不多说,先来第一部分

一、原型链

要想手画出来原型,首先的画出来一个小原型

根据这张图科研看出来,每一个函数都可以通过new方法创建出一个实例。函数本身则有一个显示原型,实例本身又会指向函数的显示原型,也就是说,函数new出来的实例的隐式原型指向与函数本身的显示原型指向是一直的 而函数的原型身上有一个方法constructor方法,它指向函数本身,于是一个基础班的原型图就出现了,后续所有的原型图都以此为基础
那么函数原型怎么来的呢?在js中,函数是对象,对象也是函数,所以存在一个Object创建的实例,于是在第一个的基础上第二个原型图就出现了

刚刚也提到,函数是对象,对象也是函数,那么object是否也是某个函数的实例呢?函数又是怎么来的呢,那么就引出了最终的原型链

我们知道,函数又可以携程new Function的形式,所以所有的函数都可以使用这样的方式来写,所以的函数都是Function的实例,而在js中Fcuntion的显示原型和隐士原型都是特定的指向function原型而new出来的函数也有一个隐士原型,指向function原型,此时下方的三角形成闭环而object他的隐式原型也指向function原型,因为对象也是函数,最后function隐士原型又指向object原型。而object原型指向为空 最后完整的原型图

二、手写数组扁平化

js 复制代码
针对数组扁平化我相信大家一定不陌生,就是把多维数组展开形成一维数组,下面介绍两种方式
**1 flat方法**
在es6以后出现了Array.prototype.flat()方法,用来处理数组扁平化的,可以传入一个参数表示展开几层,不传只会默认展开1层
let arr = [1, 2, [2, 3, 4, [2, 1], 2, [2, 3]]]

 //flat 方法
 let a=arr.flat()
 //[ 1, 2, 2, 3, 4, [ 2, 1 ], 2, [ 2, 3 ] ]
 let s = arr.flat(2)
 console.log(s)
 //[
  1, 2, 2, 3, 4,
  2, 1, 2, 2, 3
]

//reduce实现flat函数
//reduce就是一个累加器函数,会返回新的数组,在这里可以通过这样的方式进行扁平化
function Myflat(arr) {
    return arr.reduce((pre, cur) => {
        if (Array.isArray(cur)) {
                //如果第二个数还是数组的话,那么使用apply进行递归将递归后的数加入到pre中
            pre.push.apply(pre, Myflat(cur))
        } else {
        //如果不是数组则直接加入到pre中
            pre.push(cur)
        }
        return pre
    }, [])
}
console.log(Myflat(arr));
//[
  1, 2, 2, 3, 4,
  2, 1, 2, 2, 3
]

三、手写group by方法

兼容问题

js 复制代码
Object.groupby()方法,可以根据某一对象的属性进行分类处理
groupby方法传入两个参数,一个是数组对象,一个是函数返回的要根据谁进行分类
const inventory = [{
    name: "芦笋",
    type: "蔬菜",
    quantity: 5,
    pice: 200
  },
  {
    name: "香蕉",
    type: "水果",
    quantity: 0,
    pice: 100
  },
  {
    name: "山羊",
    type: "肉",
    quantity: 23,
    pice: 200
  },
  {
    name: "樱桃",
    type: "水果",
    quantity: 5,
    pice: 300
  },
  {
    name: "鱼",
    type: "肉",
    quantity: 22,
    pice: 500
  },
];

let result = Object.groupBy(inventory, (e) => e.type)
console.log(result);
js 复制代码
接下来是手写该方法
还是通过reduce方法,reduce会返回一个新的数组,groupby传入的第二个参数是一个函数,拿到函数的返回值就是要根据谁进行分组
function groupBy(array, keyFn) {
  return array.reduce((acc, item) => {
    const key = keyFn(item);//分组的key
    if (!acc[key]) {
    //如果对象里不存在这个类别则创建一个并赋值为空数组
      acc[key] = [];
    }
    //将对应类别的数据加入到里面
    acc[key].push(item);
    return acc;
  }, {});//效果是一样的
}

下面还有一个简化版的对象分类方法
function gb(obj, gbroup) {
//传入数据和分组的类别
// let result = {}
// for (let i = 0; i < obj.length; i++) {
//   let s = obj[i]
//   let fen = s[gbroup] //拿到对应分类的数据作为分类的key
//   if (result[fen]) {
      //如果存在对应的key值,则push
//        result[fen].push(obj[i])
//   } else {
//如果不存在则把值放入数组添加到对象里
//        result[fen] = [obj[i]]
//   }
// }
// return result
// }
{
  '100': [ { name: '香蕉', type: '水果', quantity: 0, pice: 100 } ],
  '200': [
    { name: '芦笋', type: '蔬菜', quantity: 5, pice: 200 },
    { name: '山羊', type: '肉', quantity: 23, pice: 200 }
  ],
  '300': [ { name: '樱桃', type: '水果', quantity: 5, pice: 300 } ],
  '500': [ { name: '鱼', type: '肉', quantity: 22, pice: 500 } ]
}

四、函数防抖

js 复制代码
大家更不陌生了,防抖函数,该函数会返回一个函数,返回的函数就是经过防抖处理的,同时使用了apply,防止传入的参数丢失
function devounce2(fn, time = 1000) {
  let timer
  return function (...arges) {
    clearInterval(timer)
    timer = setTimeout(() => {
      fn.apply(this, arges)
    }, time)
  }
}

五、函数节流

js 复制代码
传统的函数节流存在两种方式
最常见的,但是无法立即执行一次
function throttle(callback, time = 100) {
    let timer //不会立即执行
    return function () {
        if (timer) {
            return //这里是与防抖的区别,如果已经存在了就不再执行了,而是相隔固定的秒数固定执行
        }
        let arg = arguments
        timer = setTimeout(() => {
            callback.apply(this, arg)
            timer = null
        }, time);
    } 
}
第二种 时间戳的方式,该方法会立即执行一次。原因在于首次t为空,所以会立即执行一次回调函数,通过判断当前时间与指定的间隔时间的差值来判断
function throttle2(callback, time = 100) {
    let t //会立即执行一次
    return function () {
        if (!t || Date.now() - t >= time) {
            callback.apply(null, arguments)
            t = Date.now()
        }
    }
}
第三种,二者的结合允许自定义是否首次执行
function throttle3(callback, time = 100, immediate) {
    //整合版,允许自定义控制是否立即执行
    if (immediate === undefined) {
    //默认首次要执行
        immediate = true
    }
    if (immediate) {
        let t;
        return function () {
            if (!t || Date.now() - t >= time) {
                callback.apply(null, arguments)
                t = Date.now()
            }
        }
    } else {
        let timer;
        return function () {
            if (timer) {
                return
            }
            let arg = arguments
            timer = setTimeout(() => {
                callback.apply(this, arg)
                timer = null
            }, time);
        }
    }

}

六、手写arr[-1]

js 复制代码
大家都知道,arr[-1]会默认取数组的最后一项,那么来看看他是如何实现的吧
Proxy代理大家肯定不陌生,实现这个的原理就是使用代理,通过代理来实现再读取的时候会触发代理,逻辑在这里处理
let arr = [1, 2, 3, 4, 5]
const proxyAry = (arr) => {
    const len = arr.length
    return new Proxy(arr, {
        get(target, key) {
            key = +key
            if (key < 0) {
                key += len
            }
            return target[key]
        }
    })
}
let s = proxyAry(arr)
console.log(s[-2]);

本次分析就到这里啦,感兴趣的可以取我的GitHub取源码:

地址:github.com/dizao006/--...

相关推荐
undefined&&懒洋洋7 分钟前
Web和UE5像素流送、通信教程
前端·ue5
大前端爱好者2 小时前
React 19 新特性详解
前端
随云6322 小时前
WebGL编程指南之着色器语言GLSL ES(入门GLSL ES这篇就够了)
前端·webgl
随云6322 小时前
WebGL编程指南之进入三维世界
前端·webgl
J老熊2 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
我爱学Python!3 小时前
面试问我LLM中的RAG,秒过!!!
人工智能·面试·llm·prompt·ai大模型·rag·大模型应用
OLDERHARD3 小时前
Java - LeetCode面试经典150题 - 矩阵 (四)
java·leetcode·面试
寻找09之夏3 小时前
【Vue3实战】:用导航守卫拦截未保存的编辑,提升用户体验
前端·vue.js
非著名架构师3 小时前
js混淆的方式方法
开发语言·javascript·ecmascript
银氨溶液4 小时前
MySql数据引擎InnoDB引起的锁问题
数据库·mysql·面试·求职