前端开发应该掌握的手写代码

1. 模拟call

Function.prototype._call = function (context, ...args) {
	if (typeof this !== 'function') {
		return
	}
	context = context || window
	const s = Symbol();
	context[s] = this
	const res = context[s](...args)
	delete context[s]
	return res
}

2. 模拟apply

Function.prototype._apply = function (context) {
	if (typeof this !== 'function') {
		return
	}
	context = context || window
	const args = arguments[1]
	const s = Symbol();
	context[s] = this
	const res = args ? context[s](...args) : context[s]()
	delete context[s]
	return res
}

3. 模拟bind

Function.prototype._bind = function (context) {
	if (typeof this !== "function") {
		// 如果当前执行环境不是函数就抛出错误
		throw new TypeError("Error");
	}
	let _this = this;
	let arg = [...arguments].slice(1);
	return function F() {
		// 如果函数使用new的情况
		if (this instanceof F) {
			return new _this(...arg, ...arguments);
		} else {
			return _this.apply(context, arg.concat(...arguments));
		}
	};
}

4. 模拟new

function _new(fn, ...arg) {
	const obj = Object.create(fn.prototype);
	const ret = fn.apply(obj, arg);
	return ret instanceof Object ? ret : obj;
}

5. 模拟intanceof

function _instanceof(L, R) {
		// 验证如果为基本数据类型,就直接返回false
		const baseType = ['string', 'number', 'boolean', 'undefined', 'symbol']
		if (baseType.includes(typeof (L))) { return false }
		let RP = R.prototype;  //取 R 的显示原型
		L = L.__proto__;       //取 L 的隐式原型
		while (true) {         
			if (L === null) {    //找到最顶层
				return false;
			}
			if (L === RP) {       //严格相等
				return true;
			}
			L = L.__proto__;  //没找到继续向上一层原型链查找
		}
	}

6. 函数防抖

function debounce(fn, delay = 100) {
	let timer = null
	return function (...args) {
		clearTimeout(timer)
		timer = setTimeout(() => {
			return fn.call(this, ...args)
		}, delay);
	}
}

7. 函数节流

//时间戳版
function throttle1(fn, delay = 100) {
	let lastTime = 0
	return function () {
		let nowTime = +new Date()
		if (nowTime - lastTime > delay) {
			lastTime = nowTime
			return fn.apply(this, arguments)
		}
	}
}

//定时器版
function throttle2(fn, delay = 100) {
	let timer = null
	return function() {
		let args = arguments
		if (!timer) {
			setTimeout(() => {
				fn.applay(this, args)
				timer = null
			}, delay)
		}
	}
}

8. 数组拍平(扁平化)

function flatten1(arr) {
	let result = [];
	for (let i = 0; i < arr.length; i++) {
		if (Array.isArray(arr[i])) {
			result = result.concat(flatten(arr[i]))
		} else {
			result.push(arr[i])
		}
	}
	return result;
}


function flatten2(arr) {
	return arr.reduce((pre, cur) => {
		return Array.isArray(cur) ? pre.concat(flatten2(cur)) :  pre.concat(cur)
	}, [])
}

9. 函数柯里化

所谓柯里化就是把具有较多参数的函数转换成具有较少参数的函数的过程

function curry(fn, ...args) {
	let length = fn.length;
	args = args || [];
	return function (...rest) {
		let _args = [...args, ...rest];
		return _args.length < length
			? curry(fn, _args)
			: fn.apply(this, _args);
	}
}
// let fn = curry(function (a, b, c) {
// 	console.log(a + b + c);
// });
// fn('a', 'b', 'c'); // abc
// fn('a', 'b')('c'); // abc
// fn('a')('b')('c'); // abc

10. 发布订阅EventEmitter

class EventEmitter {
	subs = {}
	emit(event, ...args) {
		if (this.subs[event] && this.subs[event].length) {
			this.subs[event].forEach(cb => cb(...args))
		}
	}
	on(event, cb) {
		(this.subs[event] || (this.subs[event] = [])).push(cb)
	}
	off(event, offCb) {
		if (offCb) {
			if (this.subs[event] && this.subs[event].length)
				this.subs[event] = this.subs[event].filter(cb => cb !== offCb)
		} else {
			this.subs[event] = []
		}
	}
	once(event, cb) {
		let wrapFun = function (...args) {
			cb(...args);
			this.off(event, wrapFun);
		};
		this.on(event, wrapFun);
	}
}

11. 图片懒加载

(1)将资源路径赋值到img标签的data-xx属性中,而不是src属性
(2)获取img节点距离浏览器顶部的距离,如果小于或等于浏览器窗口的可视高度,那么就将data-xx的值赋值到src里去

// 图片懒加载类
class LazyLoad {
	constructor(el) {
		this.imglist = Array.from(document.querySelectorAll(el)); // 需使用懒加载的图片集合
		this.init(); // 初始化
	}
	// 判断是否该图片是否可以加载
	canILoad() {
		let imglist = this.imglist;
		for (let i = imglist.length; i--;) {
			// 缩写,相当于if true
			this.getBound(imglist[i]) && this.loadImage(imglist[i], i);
		}
	}
	// 获取图片与窗口的信息
	getBound(el) {
		let bound = el.getBoundingClientRect();
		let clientHeight = window.innerHeight;
		// 图片距离顶部的距离 <= 浏览器可视化的高度,从而推算出是否需要加载
		return bound.top <= clientHeight - 100; // -100是为了看到效果,您也可以去掉
	}
	// 加载图片
	loadImage(el, index) {
		// 获取之前设置好的data-original值
		let src = el.getAttribute('data-original');
		// 赋值到src,从而请求资源
		el.src = src;
		// 避免重复判断,已经确定加载的图片应当从imglist移除
		this.imglist.splice(index, 1);
	}
	// 当浏览器滚动的时候,继续判断
	bindEvent() {
		window.addEventListener('scroll', () => {
			this.imglist.length && this.canILoad();
		});
	}
	// 初始化
	init() {
		this.canILoad();
		this.bindEvent();
	}
}
// 实例化对象,参数则是需要使用懒加载的图片类名
const lazy = new LazyLoad('.lazyload')

12. 实现promise

;(function (window) {

	const PENDING = 'pending'
	const RESOLVED = 'resolved'
	const REJECTED = 'rejected'

	function Promise(excutor) {
		const self = this
		self.status = PENDING
		self.data = undefined
		self.callbacks = [] // 存放then中回调函数对象的数组 [{onResolved, onRejected}] 成功的回调和失败的回调

		function resolve(value) {
			if (self.status !== PENDING) { // 只能从pending改为resolved
				return
			}
			self.status = RESOLVED
			self.data = value
			if (self.callbacks.length > 0) { // 如果之前保存有状态改变时的回调函数就执行
				self.callbacks.forEach(callbackObj => {
					setTimeout(() => { //让回调函数异步执行
						callbackObj.onResolved(value)
					})
				})
			}
		}

		function reject(reason) {
			if (self.status !== PENDING) {
				return
			}
			self.status = REJECTED
			self.data = reason
			if (self.callbacks.length > 0) { // 如果之前保存有状态改变时的回调函数就执行
				self.callbacks.forEach(callbackObj => {
					setTimeout(() => {
						callbackObj.onRejectd(reason)
					})
				})
			}
		}

		try {
			excutor(resolve, reject) // 同步执行执行器回调函数
		} catch (error) {
			reject(error)  // 如果存在异常执行reject抛出
		}

	}

	// Promise 原型对象的then(),指定promise成功和失败的回调函数,返回一个新的promise对象
	Promise.prototype.then = function (onResolved, onRejectd) {
		// 向后传递成功的value
		onResolved = typeof onResolved === 'function' ? onResolved : value => value
		// 指定默认的失败的回调(实现错误/异常传透的关键点)  写then函数时,通常可以不传第二个onRejected参数
		onRejectd = typeof onRejectd === 'function' ? onRejectd : reason => { throw reason }
		const self = this
		// then 返回的还是一个promise对象,这样才可以链式调用
		return new Promise((resolve, reject) => {
			//调用指定回调函数处理,根据执行结果改变return 的promise的状态
			function handleCb(callback) {
				/*
					1) 如果抛出异常,return的promise就会失败,reason就是error
					2)如果回调函数返回的不是promise,return的promise就会成功,value就是返回值
					3)如果回调函数返回的是promise,return的promise结果就是这个promise的结果
				*/
				try {
					const res = callback(self.data)
					if (res instanceof Promise) {
						// res.then(
						// 	value => resolve(value),  // 当res成功时,让return的promise也成功
						// 	reason => reject(reason)  // 当res失败时,让return的promise也失败
						// )
						res.then(resolve, reject)
					} else { // 如果返回的不是一个promise对象
						resolve(res)
					}
				} catch (error) {
					reject(error)
				}
			}

			if (self.status === PENDING) {
				// 如果当前的状态还是pending, 先把回调函数保存起来
				self.callbacks.push({
					onResolved(value) {
						handleCb(onResolved)
					},
					onRejectd(reason) {
						handleCb(onRejectd)
					}
				})
			} else if (self.status === RESOLVED) { // 如果当前的状态变为resolved
				setTimeout(() => { //异步执行
					handleCb(onResolved)
				})
			} else { // 如果当前的状态变为rejected
				setTimeout(() => {
					handleCb(onRejectd)
				})
			}
		})
	}

	// Promise 原型对象的catch(),指定promise失败的回调函数,返回一个新的promise对象
	Promise.prototype.catch = function (onRejectd) {
		return this.then(undefined, onRejectd)
	}

	Promise.resolve = function (value) {
		// 返回一个成功/失败的promise
		return new Promise((resolve, reject) => {
			// value是promise
			if (value instanceof Promise) {
				// 使用value的结果作为promise的结果
				value.then(resolve, reject)
			} else {
				// 如果value不是一个promise,promise变为成功,数据是value
				resolve(value)
			}
		})
	}

	Promise.reject = function (reason) {
		// 返回一个失败的promise
		return new Promise((resolve, reject) => {
			reject(reason)
		})
	}

	Promise.all = function (promises) {
		return new Promise((resolve, reject) => {
			const values = new Array(promises.length)
			let resolveCount = 0
			promises.forEach((p, i) => {
				// 有可能传入的数组不全是promise对象,比如[p1,2,p3],所以在外包一层Promise.resolve(),确保返回的是promise
				Promise.resolve(p).then(value => {
					resolveCount++
					values[i] = value
					if (resolveCount === promises.length) { // 如果全部成功了,return的promise才成功
						resolve(values)
					}
				}, reason => { // 只要一个失败了,return的promise就返回失败
					reject(reason)
				})
			})
		})
	}

	Promise.race = function (promises) {
		return new Promise((resolve, reject) => {
			promises.forEach((p, i) => {
				// 有可能传入的数组不全是promise对象,比如[p1,2,p3],所以在外包一层Promise.resolve(),确保返回的是promise
				Promise.resolve(p).then(value => {
					resolve(value) // 一旦有一个promise成功了,return的promise就成功
				}, reason => {
					reject(reason) // 一旦有一个promise失败了,return的promise就失败
				}
				)
			})
		})
	}

	window.Promise = Promise
})(window)

13.实现一个简易路由

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值