这是前端基础回顾的第三篇,记录一下一些手写源码的例子。
开始
深拷贝
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 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 }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 }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 ) { } } 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 = intervalfunction 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输入到页面展现到底发生什么?