前言:中级前端必需要会的手写常用API,平时开发的时候我们要思考一下用到的api 是怎么实现的,原理是什么,这样再遇到兼容性问题的情况,就可以自己去实现一个polyfill 去做兼容。而这也是一些面试大厂经常会问到的一些问题。就算用不到,但也得知道😂 , 欢迎留言,持续更新中....
1. new 的内部机制,自己实现一个new。
/*
思路:new 做了什么?
1\. 创建一个对象,使得创建的对象的__proto__ === Fn.prototype
2\. 调用Fn 的构造函数。传入的this 是创建的对象。
3\. 如果Fn 的构造函数返回了一个对象,则返回这个对象,如果是普通类型的值,返回新建的对象。
*/
function person(options) {
this.name = options.name
// return this.name 或者 {} 可以自己试试啥效果😂
}
function myNew(fn, options) {
var obj = {}
obj.__proto__ = fn.prototype
var res = fn.call(obj, options)
if (typeof res === 'object') {
return res
}
return obj
}
var p = myNew(person, { name: '小强' }) // person {name: "小强"}
var p2 = new person({ name: '小强' }) // person {name: "小强"}
2. instanceof 是用来干什么的,实现一个instanceof。
/*
思路:x instanceof y ,查询x 的原型链上是否有 y.prototype, 也就是是否有
x.__proto__.__proto__.... === y.prototype 这样的等式成立, 如果有就返回true,否则返回false
也就是 x 是否 y 的子类。
1\. 循环判断 x.__proto__.__proto__ === y.prototype
2\. 如果 x.__proto__.__proto__ === null 都没发现 y.prototype 就返回false
*/
function myInstanceOf(x, y) {
let _proto = x.__proto__
while(_proto !== null) {
if (x.__proto__ === y.prototype) {
return true
}
_proto = _proto.__proto__
}
return false
}
myInstanceOf([], Array) // true
myInstanceOf({}, Array) // false
3. 实现一个promise
/*
思路:
1\. promise 是一个微任务对象,接收一个函数参数fn, fn 需要一个resolve,一个reject 函数。
2\. then 和 catch 的作用其实是注册fulfilled 和 rejected 状态时候要执行的事件。
3\. then 接收0个或者一个或者两个参数,第一个参数是 resolve 成功之后执行的函数,如果有返回值,
可以接着.then ,所以需要在.then 函数里返回this, 第二个参数是 reject 失败之后执行的函数
也需要返回this
4\. promise 类有三个属性,一个是status 状态,pendding ,fulfilled , rejected ,状态的改变
是不可逆的,只能是pendding => fulfilled , pendding => rejected 一个是 reason,
保存失败的原因。 一个是then 的返回值 value。
5\. then 会执行所有注册的函数,某一个有返回值,后面的函数的参数都会改变,reject 注册的函数
只会执行第一个,后面再注册的不会执行(实测😄)。
*/
class Promise {
constructor(exector) {
this.status = 'pendding'
this.reason = ''
this.value = ''
this.fulfilledCallBackList = []
this.rejectedCallBack = () => {}
let resolve = (value) => {
if (this.status === 'pendding') {
this.status = 'fulfilled'
this.value = value
console.log(this.fulfilledCallBackList[0])
this.fulfilledCallBackList.forEach((fn) => {
let res = fn(this.value)
console.log(this.value)
if (res) {
this.value = res
} else {
return
}
})
}
}
let reject = (reason) => {
if (this.status === 'pendding') {
this.status = 'rejected'
this.reason = reason
this.rejectedCallBack(this.reason)
}
}
exector(resolve, reject)
}
then(resolveFn, rejectFn) {
if (resolveFn) {
this.fulfilledCallBackList.push(resolveFn)
}
if (rejectFn) {
!this.callBackCallBack &&
(this.callBackCallBack = rejectFn)
}
return this
}
catch(rejectFn) {
if (rejectFn) {
!this.callBackCallBack &&
(this.callBackCallBack = rejectFn)
}
return this
}
}
4. 函数柯里化,实现一个函数使得 add(1, 2) = 3 && add(1)(2) = 3 && add(1)(2)(3) = 6
/*
思路:
1\. 要做的能在函数执行后还能继续执行,需要返回一个函数,在最后一次的时候通过toString来
展示函数的toString 形式。
2\. 因为参数不确定,所以每次执行都要把新的参数记录下来,所以需要用到闭包。
*/
function add() {
let args = []
args.push(...arguments)
let fn = () => {
args.push(...arguments)
return fn
}
fn.toString = () => {
return args.reduce((t, i) => t + i, 0)
}
return fn
}
5.eventBus 的实现
/*
思路:
1\. eventBus 有三个核心函数,分别是 on,emit,off
*/
function eventBus () {
this.eventDict = {}
this.on = function (eventName, fn) {
this.eventDict[eventName] = this.eventDict[eventName] || []
this.evnetDict[eventName].push[fn]
}
this.emit = function (eventName) {
this.eventDict[eventName].forEach((fn) => {
fn()
})
}
this.off = function (eventName, fn) {
let i = this.eventDict.indexOf(fn)
if (i > 0) {
this.eventDict.splice(i, 1)
}
}
}
6. 防抖
/*
思路:防抖是一段时间内多次调用某个函数,最后一次调用生效,其他的不生效。
1\. 因为要记录当前是否有还没执行的fn,需要用到闭包
*/
function debounce(fn, interval) {
let timer = null
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
}, interval)
}
}
let de = debounce(() => { console.log('test') }, 1000)
de()
de()
de()
de()
de()
de() // test
7. 节流
/*
思路:节流是一段时间内函数只执行一次或者多次。
1\. 因为也要记录当前是否有还没执行的fn,也需要用到闭包
*/
function throttle(fn, timer) {
let flag = false
return function () {
if (!flag) fn()
flag = true
setTimeout(() => {
flag = false
}, timer)
}
}
let th = throttle(() => { console.log('test') }, 1000)
th() // test
th()
th()
th()
setTimeout(() => {
th() // test
}, 1000)
8.数组去重
/*
思路:有好多种方法。
1\. 利用reduce 方法,遍历一遍数组,找出不重复的组合起来。
2\. 利用 ES6 Set 类型 和 数组的 Array.from 方法去重
3\. 其他的都差不多思路😂
*/
function uniqueArray(arr) {
return arr.reduce((t, i) =>
[].concat(t.indexOf(i) !== -1 ? t : [...t, i])
, [])
}
function uniqueArray(arr) {
return Array.from(new Set(arr))
}
9. 数组扁平化
/*
思路:
1\. 数组可能有好几层,想到的是递归,不知道还有没有其他的好方法。
*/
function flatten(arr) {
return arr.reduce((t, i) =>
[].concat(t, Array.isArray(i) && flatten(i) || i)
, [])
}
flatten([1,2,3,4,[5,6,7,[8]]])
// [1, 2, 3, 4, 5, 6, 7, 8]
10. 实现数组方法 splice
/*
思路:splice 是数组原型链上的方法,所以我们要放到Array.prototype 上。
1\. splice 接受参数。分别是 [start,[deleteCount, [item1, [item2, [...itemn]]]]]
2\. start 是开始删除的位置,deleteCount 代表删除几个元素,如果不传,就代表删除start 开始
的所有元素,item1... itemn代表增加的元素
*/
Array.prototype.splice = function (
start = 0,
deleteCount = this.length - start,
...args
) {
console.log('[this start]', this)
let _arr = this
let result = [].concat(
_arr.slice(0, start),
args,
_arr.slice(start + deleteCount)
)
let res = _arr.slice(start, start + deleteCount)
result.map((item, i) => {
this[i] = item
})
this.length = result.length
console.log('[this end]', this)
return res
}
未完待续...