前言
文中代码对应的详细注释和具体使用方法都放在我的 github 上,源代码在底部连接
1.判断对象的数据类型
![](https://img.haomeiwen.com/i17912419/f86c71a3e94c5aa2.png)
使用 Object.prototype.toString 配合闭包,通过传入不同的判断类型来返回不同的判断函数,一行代码,简洁优雅灵活(注意传入 type 参数时首字母大写)
不推荐将这个函数用来检测可能会产生包装类型的基本数据类型上,因为 call 会将第一个参数进行装箱操作
2. 循环实现数组 map 方法
![](https://img.haomeiwen.com/i17912419/fa42db8770100fee.png)
使用方法:将 selfMap 注入到 Array.prototype 上(下面数组的迭代方法同理)
![](https://img.haomeiwen.com/i17912419/fe5b1a028b7f5772.png)
值得一提的是,map 的第二个参数为第一个参数回调中的 this 指向,如果第一个参数为箭头函数,那设置第二个 this 会因为箭头函数的词法绑定而失效
另外就是对稀疏数组的处理,通过 hasOwnProperty 来判断当前下标的元素是否存在与数组中(感谢评论区的朋友)
3. 使用 reduce 实现数组 map 方法
![](https://img.haomeiwen.com/i17912419/d912c24d865434fa.png)
4. 循环实现数组 filter 方法
![](https://img.haomeiwen.com/i17912419/0202b139dfe97198.png)
5. 使用 reduce 实现数组 filter 方法
![](https://img.haomeiwen.com/i17912419/07e179db387211e4.png)
6. 循环实现数组的 some 方法
![](https://img.haomeiwen.com/i17912419/157351dc660b4db0.png)
执行 some 方法的数组如果是一个空数组,最终始终会返回 false,而另一个数组的 every 方法中的数组如果是一个空数组,会始终返回 true
7. 循环实现数组的 reduce 方法
![](https://img.haomeiwen.com/i17912419/c26a3087d29a457a.png)
因为可能存在稀疏数组的关系,所以 reduce 需要保证跳过稀疏元素,遍历正确的元素和下标(感谢@神三元的提供的代码)
8. 使用 reduce 实现数组的 flat 方法
![](https://img.haomeiwen.com/i17912419/d379b3e29c7c23ed.png)
因为 selfFlat 是依赖 this 指向的,所以在 reduce 遍历时需要指定 selfFlat 的 this 指向,否则会默认指向 window 从而发生错误
原理通过 reduce 遍历数组,遇到数组的某个元素仍是数组时,通过 ES6 的扩展运算符对其进行降维(ES5 可以使用 concat 方法),而这个数组元素可能内部还嵌套数组,所以需要递归调用 selfFlat
同时原生的 flat 方法支持一个 depth 参数表示降维的深度,默认为 1 即给数组降一层维度
![](https://img.haomeiwen.com/i17912419/eb5bb71b9df17cf7.png)
传入 Inifity 会将传入的数组变成一个一维数组
![](https://img.haomeiwen.com/i17912419/0a2f13c4f7142159.png)
原理是每递归一次将 depth 参数减 1,如果 depth 参数为 0 时,直接返回原数组
9. 实现 ES6 的 class 语法
![](https://img.haomeiwen.com/i17912419/8bb455dc5bb756c1.png)
ES6 的 class 内部是基于寄生组合式继承,它是目前最理想的继承方式,通过 Object.create 方法创造一个空对象,并将这个空对象继承 Object.create 方法的参数,再让子类(subType)的原型对象等于这个空对象,就可以实现子类实例的原型等于这个空对象,而这个空对象的原型又等于父类原型对象(superType.prototype)的继承关系
而 Object.create 支持第二个参数,即给生成的空对象定义属性和属性描述符/访问器描述符,我们可以给这个空对象定义一个 constructor 属性更加符合默认的继承行为,同时它是不可枚举的内部属性(enumerable:false)
而 ES6 的 class 允许子类继承父类的静态方法和静态属性,而普通的寄生组合式继承只能做到实例与实例之间的继承,对于类与类之间的继承需要额外定义方法,这里使用 Object.setPrototypeOf 将 superType 设置为 subType 的原型,从而能够从父类中继承静态方法和静态属性
10. 函数柯里化
![](https://img.haomeiwen.com/i17912419/35dcfb811de20c66.png)
使用方法:
![](https://img.haomeiwen.com/i17912419/e72777109fa49612.png)
柯里化是函数式编程的一个重要技巧,将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术
函数式编程另一个重要的函数 compose,能够将函数进行组合,而组合的函数只接受一个参数,所以如果有接受多个函数的需求并且需要用到 compose 进行函数组合,就需要使用柯里化对准备组合的函数进行部分求值,让它始终只接受一个参数
借用一个例子
![](https://img.haomeiwen.com/i17912419/daababcf4641345d.png)
11. 函数柯里化(支持占位符)
![](https://img.haomeiwen.com/i17912419/fb3bc8d39414acce.png)
使用方法:
![](https://img.haomeiwen.com/i17912419/5f2b50254539e305.png)
![](https://img.haomeiwen.com/i17912419/6c7651891ab69397.png)
通过占位符能让柯里化更加灵活,实现思路是,每一轮传入的参数先去填充上一轮的占位符,如果当前轮参数含有占位符,则放到内部保存的数组末尾,当前轮的元素不会去填充当前轮参数的占位符,只会填充之前传入的占位符
12. 偏函数
![](https://img.haomeiwen.com/i17912419/574d673798e910bf.png)
使用方法:
![](https://img.haomeiwen.com/i17912419/c5e7314784692d50.png)
偏函数和柯里化概念类似,个人认为它们区别在于偏函数会固定你传入的几个参数,再一次性接受剩下的参数,而函数柯里化会根据你传入参数不停的返回函数,直到参数个数满足被柯里化前函数的参数个数
Function.prototype.bind 函数就是一个偏函数的典型代表,它接受的第二个参数开始,为预先添加到绑定函数的参数列表中的参数,与 bind 不同的是,上面的这个函数同样支持占位符
13. 斐波那契数列及其优化
![](https://img.haomeiwen.com/i17912419/3493c497c4f896ee.png)
利用函数记忆,将之前运算过的结果保存下来,对于频繁依赖之前结果的计算能够节省大量的时间,例如斐波那契数列,缺点就是闭包中的 obj 对象会额外占用内存
14. 实现函数 bind 方法
![](https://img.haomeiwen.com/i17912419/1affbbe06ab72317.png)
函数的 bind 方法核心是利用 call,同时考虑了一些其他情况,例如
bind 返回的函数被 new 调用作为构造函数时,绑定的值会失效并且改为 new 指定的对象
定义了绑定后函数的 length 属性和 name 属性(不可枚举属性)
绑定后函数的原型需指向原来的函数
15. 实现函数 call 方法
![](https://img.haomeiwen.com/i17912419/87864d53632fad1a.png)
原理就是将函数作为传入的上下文参数(context)的属性执行,这里为了防止属性冲突使用了 ES6 的 Symbol 类型
16. 简易的 CO 模块
![](https://img.haomeiwen.com/i17912419/989112f6b3c3457f.png)
使用方法:
![](https://img.haomeiwen.com/i17912419/471a445887ca0ecd.png)
run 函数接受一个生成器函数,每当 run 函数包裹的生成器函数遇到 yield 关键字就会停止,当 yield 后面的 promise 被解析成功后会自动调用 next 方法执行到下个 yield 关键字处,最终就会形成每当一个 promise 被解析成功就会解析下个 promise,当全部解析成功后打印所有解析的结果,衍变为现在用的最多的 async/await 语法
17. 函数防抖
![](https://img.haomeiwen.com/i17912419/78fd60e6c3d2c941.png)
leading 为是否在进入时立即执行一次,原理是利用定时器,如果在规定时间内再次触发事件会将上次的定时器清除,即不会执行函数并重新设置一个新的定时器,直到超过规定时间自动触发定时器中的函数
同时通过闭包向外暴露了一个 cancel 函数,使得外部能直接清除内部的计数器
18. 函数节流
![](https://img.haomeiwen.com/i17912419/c0df26ac123a38e5.png)
和函数防抖类似,区别在于内部额外使用了时间戳作为判断,在一段时间内没有触发事件才允许下次事件触发,同时新增了 trailing 选项,表示是否在最后额外触发一次
19. 图片懒加载
![](https://img.haomeiwen.com/i17912419/ecf55def4de4b65d.png)
getBoundClientRect 的实现方式,监听 scroll 事件(建议给监听事件添加节流),图片加载完会从 img 标签组成的 DOM 列表中删除,最后所有的图片加载完毕后需要解绑监听事件
![](https://img.haomeiwen.com/i17912419/8f6c34ab04481be0.png)
intersectionObserver 的实现方式,实例化一个 IntersectionObserver ,并使其观察所有 img 标签
当 img 标签进入可视区域时会执行实例化时的回调,同时给回调传入一个 entries 参数,保存着实例观察的所有元素的一些状态,比如每个元素的边界信息,当前元素对应的 DOM 节点,当前元素进入可视区域的比率,每当一个元素进入可视区域,将真正的图片赋值给当前 img 标签,同时解除对其的观察
20. new 关键字
![](https://img.haomeiwen.com/i17912419/0ebf5633a65fe9a8.png)
源代码
实现代码较长,这里我直接贴上对应源代码地址 JSON.stringify,欢迎 star,以后有后续的技巧会第一时间添加新的内容
自己是一个五年的全栈工程师,这里推荐一下我的前端学习交流群:开始484然后757最后760,里面都是学习前端的,群里会不定期更新最新的教程和学习方法,有想学习web前端的,或是转行,或是大学生,还有工作中想提升自己能力的web前端党欢迎加入,
网友评论