前端基础回顾-3-手写源码

这是前端基础回顾的第三篇,记录一下一些手写源码的例子。

开始

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function deepClone(object, map=new Map()) {
if(typeof object !== 'object') {
return object
}
if(map.get(object)) return map.get(object)
let result = {}
if(Object.prototype.toString.call(object) === '[object Array]') {
result = []
}

map.set(object, result)

for(objectKey in object) {
if(object.hasOwnProperty(objectKey)) {
result[objectKey] = deepClone(object[objectKey])
}
}
return result
}

0.1 + 0.2 === 0.3

1
2
3
function isNumberEqual(number1, number2) {
return Math.abs(number1 - number2) < Math.EPSILON
}

call、apply、bind

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
// call 
Function.prototype.myCall = function(context) {
if(typeof this !== 'function') {
throw new Error('is not a function')
}
const symbol = Symbol()
const args = [...arguments].slice(1)
context = context || window
context[symbol] = this
const result = context[symbol](...args)
delete context[symbol]
return result
}

// apply
Function.prototype.myApply = function(context) {
if(typeof this !== 'function') {
throw new Error('is not a function')
}
const symbol = Symbol()
let args = [...arguments][1]
args = Array.isArray(args) ? args : []
context = context || window
context[symbol] = this
const result = context[symbol](...args)
delete context[symbol]
return result
}

// bind
Function.prototype.myBind = function(context) {
if(typeof this !== 'function') {
throw new Error('is not a function')
}
const args = [...arguments].slice(1)
context = context || window
function fn() {
const realArgs = [...args, ...arguments]
return this.call(this instanceOf fn ? this : context, ...realArgs)
}
return fn
}

new

1
2
3
4
5
6
function myNew(context) {
const obj = Object.create(null)
obj.__proto__ = context.prototype
const result = context.apply(obj, [...arguments].slice(1))
return typeof result === 'object' ? result : obj
}

instanceof

1
2
3
4
5
6
7
8
9
function myInstanceof(target, origin) {
let proto = target.__proto__
const prototype = origin.prototype
while(proto) {
if(prototype === proto) return true
proto = proto.__proto__
}
return false
}

flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Array.prototype.myFlat = function(deep) {
const origin = this
function flat(array, deep) {
let result = []
for(let i = 0; i < array.length; i ++) {
const item = array[i]
if(Array.isArray(item) && (deep > 0)) {
result.push(...flat(item, deep === Infinity ? deep : (deep - 1)))
}else {
result.push(item)
}
}
return result
}
return flat(origin, deep || 1)
}

reduce

1
2
3
4
5
6
7
8
9
10

Array.prototype.myReduce = function(handler, initialValue) {
let total = initialValue || 0
const origin = this
for(let i = 0; i < origin.length; i ++) {
total = handler(total, origin[i], i, origin)
}
return total
}

防抖和节流

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
// 节流
function throttle(func, options={}) {
const { wait=100 } = options
let done = true
return function(...args) {
if(!done) return
done = false
setTimeout(() => {
func(...args)
done = false
}, wait)
}
}

// 防抖
function debounce(func, options={}) {
const { wait=100 } = options
let timeout
let done = true
return function(...args) {
if(!done) clearTimeout(timeout)
done = false
timeout = setTimeout(() => {
func(...args)
done = true
}, wait)
}
}

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

const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

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

status = PENDING
value = undefined
reason = undefined

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

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

then = (onFulfilled, onReject) => {
if(this.status === FULFILLED) {
onFulfilled(this.value)
}else if(this.status === REJECTED) {
onReject(this.reason)
}
}

}

const promise = new MyPromise((resolve, reject) => {
if(Math.random() > 0.5) {
resolve('resolve')
}else {
reject('reject')
}
})

promise
.then(value => {
console.log(value)
}, reason => {
console.log(reason)
})

数字累加

1
2
3
function add(start, end) {
return (start + end) * (end - start + 1) / 2
}

随机范围数字

1
2
3
function randomNumber(start, end, startContain=true, endContain=true) {
const number = Math.random() * (end - start + 1) + start
}

前几个斐波那契

1
2
3
4
5
6
7
8
function fibonacci(limit) {
function cal(number) {
return number < 2 ? number : cal(number - 1) + cal(number - 2)
}
return new Array(limit).fill(0).map((_, index) => {
return cal(index + 1)
})
}

随机排序

1
2
3
4
5
6
7
8
9
function randomSort(arr) {
const length = arr.length

for(let index = 0; index < length; index ++) {
const ind = Math.floor(Math.random() * (length - index)) - index
[arr[index], [arr[ind]]] = [arr[ind], arr[index]]
}

}

通用事件模型

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
class EventUtil {

addEventListener(element, event, handler) {
if(element.addEventListener) {
element.addEventListener(event, handler)
}else if(element.attachEvent) {
element.attachEvent(`on${event}`, handler)
}else {
element[`on${event}`] = handler
}
}

removeEventListener(element, event, handler) {
if(element.removeEventListener) {
element.removeEventListener(event, handler)
}else if(element.detachEvent) {
element.detachEvent(`on${event}`, handler)
}else {
element[`on${event}`] = null
}
}

getEventTarget(event) {
return event.target || event.srcElement
}

getEvent(event) {
return event || window.event
}

stopPropagation(event) {
if(event.stopPropagation) {
event.stopPropagation()
}else {
event.cancelBubble = true
}
}

preventDefault(event) {
if(event.preventDefault) {
event.preventDefault()
}else {
event.returnValue = false
}
}

}

ajax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const xhr = new XMLHttpRequest()

xhr.open('GET', '/request', true)

xhr.onreadystatechange = function() {
if(this.readyState == 4) return

if(this.status == 200) {
// TODO
}
}

xhr.onerror = function() {

}

xhr.send()

jsonp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function jsonp(url, params, callback) {

const resonseCallbackName = 'jsonpCallback' + Date.now()

const queryString = Object.entries({
...params,
callback: resonseCallbackName
}).map(item => `${item[0]}=${item[1]}`).join('&')
let src = url + `${url.includes('?') ? '&' : '?'}${queryString}`

const script = document.createElement('script')
script.src = url

window[resonseCallbackName] = function(...args) {

callback(...args)
document.body.removeChild(script)
}

document.body.appendChild(script)

}

倒计时纠偏

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

let interval = 1000
let count = 0
let timeout = 5000
const startTime = Date.now()
let nextTime = interval

function countup() {
count ++
const offset = Date.now() - (count * interval + startTime)
nextTime = interval - offset
if(nextTime < 0) {
nextTime = 0
}

timeout -= interval

if(!timeout) {
console.log('timeout')
}else {
setTimeout(countup, nextTime)
}

}

setTimeout(countup, nextTime)

结束

结束🔚。

参考资料
从URL输入到页面展现到底发生什么?


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!