手写方法

手写 Object.create

将传入的对象作为原型

1
2
3
4
5
function create(obj) {
  function F() {}
  F.prototype = obj
  return new F()
}

手写 instanceof

instanceof 运算符用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置 实现步骤:

  1. 首先获取类型的原型
  2. 然后获得对象的原型
  3. 然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function myInstanceof(left, right) {
  let proto = Object.getPrototypeOf(left), // 获取对象的原型
    prototype = right.prototype // 获取构造函数的 prototype 对象

  // 判断构造函数的 prototype 对象是否在对象的原型链上
  while (true) {
    if (!proto) return false
    if (proto === prototype) return true

    proto = Object.getPrototypeOf(proto)
  }
}

手写 new

在调用 new 的过程中会发生以上四件事情:

  1. 首先创建了一个新的空对象
  2. 设置原型,将对象的原型设置为函数的 prototype 对象。
  3. 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
  4. 判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function myNew() {
  let newObj = null
  let constructor = Array.prototype.shift.call(arguments)
  let result = null

  // 判断参数是否是一个函数
  if (typeof constructor !== 'function') {
    throw new Error('请传入一个函数')
  }

  //新建一个空对象,对象的原型为构造函数的prototype对象
  newObj = Object.create(constructor.prototype)
  //将this指向新创建的对象,并调用构造函数
  result = constructor.apply(newObj, arguments)
  //判断构造函数是否返回了一个对象
  let flag = result && (typeof result === 'object' || typeof result === 'function')
  //判断返回结果
  return flag ? result : newObj
}

//使用方法
function Car(make, model, year) {
  this.make = make
  this.model = model
  this.year = year
}

const car1 = myNew(Car, 'Ford', 'Focus', '2015')

console.log(car1.make)

手写 Promise

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  status = PENDING
  value = null
  reason = null
  onFulfilledCallbacks = []
  onRejectedCallbacks = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      this.onFulfilledCallbacks.forEach((callback) => callback(value))
    }
  }

  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      this.onRejectedCallbacks.forEach((callback) => callback(reason))
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (reason) => {
            throw reason
          }

    const promise2 = new MyPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = onFulfilled(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            const x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }

      switch (this.status) {
        case PENDING:
          this.onFulfilledCallbacks.push(fulfilledMicrotask)
          this.onRejectedCallbacks.push(rejectedMicrotask)
          break
        case FULFILLED:
          fulfilledMicrotask()
          break
        case REJECTED:
          rejectedMicrotask()
          break
        default:
          break
      }
    })

    return promise2
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  finally(callback) {
    return this.then(
      (value) => MyPromise.resolve(callback()).then(() => value),
      (reason) =>
        MyPromise.resolve(callback()).then(() => {
          throw reason
        })
    )
  }

  static resolve(parameter) {
    if (parameter instanceof MyPromise) {
      return parameter
    }
    return new MyPromise((resolve) => {
      resolve(parameter)
    })
  }

  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason)
    })
  }

  static all(promises) {
    const result = []
    let count = 0
    return new MyPromise((resolve, reject) => {
      const addData = (index, value) => {
        result[index] = value
        count++
        if (count === promises.length) resolve(result)
      }

      promises.forEach((promise, index) => {
        if (promise instanceof MyPromise) {
          promise.then(
            (value) => {
              addData(index, value)
            },
            (reason) => {
              reject(reason)
            }
          )
        } else {
          addData(index, promise)
        }
      })
    })
  }

  static race(promises) {
    return new MyPromise((resolve, reject) => {
      promises.forEach((promise, index) => {
        if (promise instanceof MyPromise) {
          promise.then(
            (res) => resolve(res),
            (err) => reject(err)
          )
        } else {
          resolve(promise)
        }
      })
    })
  }

  static allSettled(promises) {
    return new MyPromise((resolve, reject) => {
      const res = []
      let count = 0
      const addData = (status, value, index) => {
        res[index] = {status, value}
        count++
        if (count === promises.length) {
          resolve(res)
        }
      }

      promises.forEach((promise, index) => {
        if (promise instanceof MyPromise) {
          promise.then(
            (res) => addData(FULFILLED, res, index),
            (err) => addData(REJECTED, err, index)
          )
        } else {
          addData(FULFILLED, promise, index)
        }
      })
    })
  }

  static any(promises) {
    return new MyPromise((resolve, reject) => {
      let count = 0
      promises.forEach((promise) => {
        promise.then(
          (res) => resolve(res),
          (err) => {
            count++
            if (count === promises.length) {
              reject(new Error('All promises were rejected'))
            }
          }
        )
      })
    })
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))

  if (x instanceof MyPromise) {
    x.then(resolve, reject)
  } else {
    resolve(x)
  }
}

手写防抖 debounce

函数防抖是指在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function debounce(fn, wait) {
  let timer = null
  return function () {
    let context = this
    let args = arguments

    if (timer) {
      clearTimeout(timer)
      timer = null
    }

    timer = setTimeout(() => {
      fn.apply(context, args)
    }, wait)
  }
}

手写节流 throttle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function throttle(fn, delay) {
  let curTime = Date.now()

  return function () {
    let context = this
    let args = arguments
    let nowTime = Date.now()

    if (nowTime - curTime >= delay) {
      curTime = Date.now()
      return fn.apply(context, args)
    }
  }
}

手写类型判断

1
2
3
4
5
function getType(value) {
  if (value === null) return value + ''
  if (typeof value === 'object') return Object.prototype.toString.call(value).slice(8, -1).toLowerCase()
  else return typeof value
}

手写 call

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Function.prototype.myCall = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  let args = [...arguments].slice(1)
  let result = null

  context = context || window
  context.fn = this
  result = context.fn(...args)
  delete context.fn
  return result
}

手写 apply

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Function.prototype.myApply = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }

  let result = null
  context = context || window
  context.fn = this

  if (arguments[1]) {
    result = context.fn(...arguments[1])
  } else {
    result = context.fn()
  }
  delete context.fn
  return result
}

手写 bind

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new TypeError('Error')
  }
  let args = [...arguments].slice(1)
  let fn = this

  return function Fn() {
    return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments))
  }
}

柯里化实现

1
2
3
function curry(fn, ...args) {
  return fn.length <= args.length ? fn(...args) : curry.bind(null, fn, ...args)
}

手写发布订阅模式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
class EventEmitter {
  constructor() {
    this.events = {}
  }

  //订阅
  on(eventName, callback) {
    //新事件触发newListener
    if (!this.events[eventName]) {
      if (this.eventName !== 'newListener') {
        this.emit('newListener', eventName)
      }
    }

    // 由于一个事件可能注册多个回调函数,所以使用数组来存储事件队列
    const callbacks = this.events[eventName] || []
    callbacks.push(callback)
    this.events[eventName] = callbacks
  }

  //单次订阅
  once(eventName, callback) {
    // 由于需要在回调函数执行后,取消订阅当前事件,所以需要对传入的回调函数做一层包装,然后绑定包装后的函数
    const one = (...args) => {
      callback(...args)
      this.off(eventName, one)
    }

    one.initialCallback = callback
    this.on(eventName, one)
  }

  //发布
  emit(eventName, ...args) {
    const callbacks = this.events[eventName] || []
    callbacks.forEach((cb) => cb(...args))
  }

  //取消订阅
  off(eventName, callback) {
    //防止newListener被关闭
    if (eventName === 'newListener') return

    //关闭对应callback
    const callbacks = this.events[eventName] || []
    const newCallbacks = callbacks.filter(
      (fn) => fn != callback && fn.initialCallback != callback /* 用于once的取消订阅 */
    )
    this.events[eventName] = newCallbacks
  }
}

const events = new EventEmitter()

//newLister用于监听新事件
events.on('newListener', function (eventName) {
  console.log(`eventName`, eventName)
})

events.on('hello', function () {
  console.log('hello')
})
console.log('------')

let cb = function () {
  console.log('cb')
}
events.on('hello', cb)
function once() {
  console.log('once')
}
events.once('hello', once)
events.emit('hello')

console.log('------')
events.off('hello', cb)
events.off('hello', once)
events.emit('hello')
Licensed under CC BY-NC-SA 4.0
最后更新于 Sep 05, 2022 00:00 UTC
comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计