likes
comments
collection
share

手写js函数集合(一)

作者站长头像
站长
· 阅读数 9

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

一、原型链

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

手写js函数集合(一)

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

那么函数原型怎么来的呢?在js中,函数是对象,对象也是函数,所以存在一个Object创建的实例,于是在第一个的基础上第二个原型图就出现了

手写js函数集合(一)

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

手写js函数集合(一)

我们知道,函数又可以携程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 } ]
}

四、函数防抖

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

五、函数节流

传统的函数节流存在两种方式
最常见的,但是无法立即执行一次
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]

大家都知道,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/--…