最近整理学习了一些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取源码: