参考:

func.call(obj, 1,2,3) // func 接收到的参数实际上是 1,2,3

func.call(obj, [1,2,3]) // func 接收到的参数实际上是 [1,2,3],undefined,undefined

  1. <a name="TuhAJ"></a>
  2. #### call的使用场景
  3. 对象的继承
  4. ```javascript
  5. function superClass () {
  6. this.a = 1
  7. this.print = function () {
  8. console.log(this.a);
  9. }
  10. }
  11. function subClass () {
  12. superClass.call(this)
  13. this.print()
  14. }
  15. subClass()

subClass通过call方法,继承了superClassprint方法和a变量。此外,subClass还可以扩展自己的其他方法。
借用方法
类数组如果想使用Array原型链上的方法,可以:

  1. let domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));

这样,domNodes就可以应用Array下的所有方法了。

1.2 手写apply()

1.3 手写bind()

bind()方法创建一个新的函数,在调用时设置this关键字为提供的值。并在调用新函数时,将给定参数列表作为原函数的参数序列的前若干项。
bind方法与call和apply比较类似,也能改变函数体内的this指向。不同的是bind方法的返回值是函数,并且需要稍后调用,才会执行。而apply和call则是立即调用。
如果bind的第一个参数是Null或者undefined,this就指向全局对象window。

1.4 函数柯里化

什么是柯里化

柯里化含义:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩余的参数,这个过程就称之为柯里化。

柯里化的结构

  1. // 柯里化处理的函数
  2. function add2(x) {
  3. return function (y) {
  4. return function (z) {
  5. return x + y + z
  6. }
  7. }
  8. }
  9. console.log(add2(10)(20)(30))
  10. const add3 = x => y => z => x + y + z
  11. console.log(add3(1)(2)(3))

为什么需要有柯里化?

让函数的职责单一。

  • 在函数式编程中,希望一个函数处理的问题尽可能单一,而不是将一大堆的处理过程交给一个函数来处理。

    柯里化作用

    参数复用、提前返回、延迟执行
    复用参数逻辑

  • makeAdder函数要求我们传入一个num

  • 在之后使用返回的函数时,我们不需要再继续传入num。 ```javascript function makeAdder(num) { return function(count) { return num + count } }

var add5 = makeAdder(5)

console.log(add5(10)) console.log(add5(100))

  1. <a name="pHrRR"></a>
  2. #### 柯里化函数的实现
  3. ```javascript
  4. function add1(x, y, z) {
  5. return x + y + z
  6. }
  7. // 柯里化函数的实现hyCurrying
  8. // 传入一个函数,返回一个新函数
  9. function hyCurrying(fn) {
  10. return function curried(...args) {
  11. // 判断当前已经接收的参数的个数和参数本身需要接收的参数是否一致
  12. // 获取fn参数个数--fn.length
  13. // 1.当已经传入的参数大于等于需要的参数时,就执行函数
  14. if(args.length >= fn.length) {
  15. return fn.apply(this, args)
  16. } else {
  17. // 没有达到个数时,需要返回一个新的函数,继续来接收参数
  18. return function(...args2) {
  19. // 接收到参数后,需要递归调用curried来检查函数的个数是否达到
  20. return curried.apply(this, [...args, ...args2])
  21. }
  22. }
  23. }
  24. }
  25. var curryAdd = hyCurrying(add1)
  26. console.log(curryAdd(10, 20, 30))
  27. console.log(curryAdd(10, 20)(30))
  28. console.log(curryAdd(10)(20)(30))

手写浅拷贝

Object.assign

1.2 手写深拷贝(考虑正则,日期,数组等类型)

对象相互赋值的一些关系:

  • 引入的赋值:指向同一个对象,相互之间会影响
  • 对象的浅拷贝:只是浅层的拷贝,内部引入对象时,依然会相互响应
  • 对象的深拷贝:两个对象不再有任何关系,不会相互影响

    JSON.parse实现深拷贝

  • 这种深拷贝的方式其实对于函数、Symbol等是无法处理的

  • 并且如果存在对象的循环引用,也会报错

    自定义深拷贝函数

  • 自定义深拷贝的基本功能

  • 对Symbol的key进行处理
  • 其他数据类型的值进行处理:数组、函数、Symbol、Set、Map
  • 对循环引用的处理

    1.3 手写EventHub

    1.4 手写reduce

    arr.reduce(function(prev, cur, index, arr){}, initialValue)

1.5 手写JSONP


1.6 手写new

  1. new运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一。
  2. 构造函数返回一个对象,在实例中只能访问返回的对象中的属性
  3. 构造函数返回一个基本类型,相当于没有返回值进行处理
  • new Object()的方法新建一个对象obj
  • 取出第一个参数,就是我们要传入的构造函数。此外因为shift会修改原数组,所以arguments会被去除第一个参数。
  • 将obj的原型指向构造函数,这样obj就可以访问到构造函数原型中的属性
  • 使用apply,改变构造函数this的指向到新的对象,这样obj就可以访问到构造函数中的属性
  • 返回obj
    1. function myNew() {
    2. // 1. 创建空对象
    3. let obj = new Object()
    4. // 获取第一个参数
    5. let Constructor = [...arguments][0]
    6. // 获取除掉第一个参数以外的参数
    7. let arg = [...arguments].slice(1)
    8. // 2. 空对象的原型指向构造函数的原型
    9. obj.__proto__ = Constructor.prototype
    10. // 获取外部传入构造器的返回值
    11. let ret = Constructor.apply(obj, arg)
    12. // 判断构造器返回值,如果是对象则return返回的对象
    13. return ret instanceof Object ? ret : obj
    14. }
    默写
    20220109
    点击查看【codepen】

1.7 debounce防抖

防抖原理:触发完事件n秒内不再触发该事件,才执行,否则不执行。如果在触发事件n秒内再次触发,则从新的事件触发时间开始计时。
防抖场景:

  • 登录、发短信等按钮避免用户点击太快,以至于发送了多次请求,需要防抖。(我觉得这个节流也可以)
  • 调整浏览器窗口大小时,resize次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖。
  • 文本编辑器实时保存,当无任何更改操作一秒后进行保存。

防抖重在清零clearTimeout(timer)

  1. const debounce = (func, n) => {
  2. let timer_id
  3. return function(){
  4. let arg = arguments
  5. clearTimeout(timer_id)
  6. timer_id = setTimeout(() => {
  7. func.apply(this, arg)
  8. }, n)
  9. }
  10. }

1.8 throttle节流

节流原理:如果持续触发事件,每隔一段事件,只执行一次事件。
节流场景:

  • scroll事件,每隔一秒计算一次位置信息
  • 浏览器播放事件,每隔一秒计算一次进度
  • input框实时搜索并发送请求展示下拉列表,每隔一秒发送一次请求(也可做防抖)

节流重在开关锁timer=null

  1. const throttle = (func, wait) => {
  2. let timer_id
  3. return function() {
  4. let args = arguments
  5. if (!timer_id) {
  6. timer_id = setTimeout(() => {
  7. timer_id = null
  8. func.apply(this, args)
  9. }, wait)
  10. }
  11. }
  12. }

2. Promise

2.1 实现 Promise 同步立即返回状态的函数:输入一个promise,立即返回当前状态(pending,resolve,reject)

2.2 手写promise.all

  1. // 判断是不是promise
  2. const isPromise = (value) => {
  3. if((typeof value === 'object' && value !== null) || typeof value === 'function') {
  4. if(typeof value.then === 'function') {
  5. return true
  6. }
  7. else {
  8. return false
  9. }
  10. }
  11. }
  12. Promise.all = function (values) {
  13. // 返回一个then,所以返回一个new Promise
  14. return new Promise((resolve, reject) => {
  15. let arr = [], index = 0 // 解决多个异步的并发问题,要使用计数器
  16. function processData(key, value) {
  17. index++
  18. arr[key] = value
  19. if(index == values.length){
  20. resolve(arr)
  21. }
  22. }
  23. // 因为最终同步执行,所以要依次把数组中的值取出来,所以要用一个for循环
  24. for (let i=0; i<values.length; i++) {
  25. let current = values[i]
  26. if(isPromise(current)) {
  27. current.then((data) => {
  28. processData(i, data)
  29. }, reject)
  30. } else {
  31. processData(i, current)
  32. }
  33. }
  34. })
  35. }
  36. // 类上的方法叫静态方法 全部成功就成功,有任何一个失败就失败了
  37. Promise.all([1,2,3,6, 7]).then(data => {
  38. console.log(data);
  39. })

2.3 promise实现sleep

2.4 一道 promise 的题,要求实现一个请求重发器,就是一旦失败就不断的重新请求直到请求超过最大次数限制,每次请求之间有固定的时间间隔

2.5 并发Promise,一次并发6次,如果中途reject一次,整体函数返回Promise.reject。整体成功返回成功的结果列表。

  1. /**
  2. *
  3. * @param {Array<Promise>} asyncList
  4. */
  5. async function promiseAll(asyncList) {
  6. let list = [];
  7. let res = [];
  8. for (let i = 0; i < asyncList.length;) {
  9. if (i + 6 < asyncList.length) {
  10. list = asyncList.slice(i, i+ 6);
  11. i += 6;
  12. }
  13. else {
  14. list = asyncList.slice(i);
  15. i = asyncList.length;
  16. }
  17. try {
  18. let _res = await Promise.all(list);
  19. res = res.concat(_res);
  20. } catch (error) {
  21. return Promise.reject(error);
  22. }
  23. }
  24. return res;
  25. }

3. 设计模式

3.1 实现一个简单的发布订阅者模式

3.2 手写一个基于发布订阅模式的js事件处理中心(EventEmitter)


4. webpack

4.1 手写loader

4.2 手写plugin

5. React

5.1 react hook 实现计数器