1.compose函数
介绍:将需要嵌套执行的函数扁平化处理。嵌套执行指的是,一个函数的返回值将作为另一个函数的参数。
compose函数的参数:若干个函数
compose函数的返回值:一个函数
简而言之:compose可以把类似于f(g(h(x)))这种写法简化成compose(f, g, h)(x)
function compose(...arg) {
const anony = function (val) {
if(arg.length === 0) return val
if(arg.length === 1) return arg[0](val)
return arg.reduce((pre, cur) => {
if (typeof pre === 'function') {
return cur(pre(val))
} else {
return cur(pre)
}
})
}
return anony
}
function fun1(val) {
return val + 1
}
function fun2(val) {
return val + 2
}
function fun3(val) {
return val + 3
}
console.log('结果', compose(fun1, fun2, fun3)(0))
// 结果 6
2.setTimeout实现setInterval
function mySetInterval(fun, delay) {
let timeoutId = null
function interval() {
fun()
timeoutId = setTimeout(interval, delay)
}
interval()
return {
claerMySetInterval: (timeoutId) => {
clearTimeout(timeoutId)
}
}
}
function foo() {
console.log('aaa')
}
mySetInterval(foo, 1000)
3.发布订阅模式
原理介绍:zhuanlan.zhihu.com/p/415653599
class EventEmitter {
constructor() {
this.events = {}
}
// 注册订阅事件
on(eventName, handler) {
if(this.events[eventName]) {
this.events[eventName].push(handler)
} else {
this.events[eventName] = [handler]
}
}
// 删除订阅事件
off(eventName, handler) {
if(this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter((handlerItem) => {
return handlerItem !== handler
})
} else {
return
}
}
// 执行订阅事件
emit(eventName, ...arg) {
this.events[eventName] && this.events[eventName].forEach(handlerItem => {
handlerItem.apply(this, arg)
// this.events[eventName].pop[handlerItem]
});
}
// 只执行一次的订阅事件
once(eventName, handler) {
function exeOnce() {
handler()
this.off(eventName, exeOnce)
}
this.on(eventName, exeOnce)
}
}
// 使用如下
const event = new EventEmitter();
const handle = (...rest) => {
console.log(rest);
};
event.on("click", handle);
event.emit("click", 1, 2, 3, 4); //[ 1, 2, 3, 4 ]
event.off("click", handle);
event.emit("click", 1, 2);
event.once("dbClick", () => {
console.log(123456);
});
event.emit("dbClick"); // 123456
event.emit("dbClick");
4.数组去重
使用Set(元素可谓基本数据类型和引用数据类型、元素不可重复)
function foo(arr) {
return [... new Set(arr)]
}
let arr = [1, 2, 2, 3, 4, 4]
console.log(foo(arr))
5. 数组扁平化
5-1. 递归实现
function flatArr(arr) {
if(!arr.length) return
return arr.reduce((preValue, curValue) =>
Array.isArray(curValue) ? [...preValue, ...flatArr(curValue)] : [...preValue, curValue],
[]
)
}
console.log(flatArr([1,2,[2,3,[5,6,7],4],[1,2,[2,1,3],3]]))
// [1, 2, 2, 3, 5, 6,7, 4, 1, 2, 2, 1,3, 3]
5-2. 迭代实现
function flatArr(arr) {
if(!arr.length) return
while(arr.some((item) => {
Array.isArray(item)
})) {
arr = [].concat(...arr)
}
}
console.log(flatArr([1,2,[2,3,[5,6,7],4],[1,2,[2,1,3],3]]))
// [1, 2, 2, 3, 5, 6,7, 4, 1, 2, 2, 1,3, 3]
6. 寄生组合继承
- 子类原型指向
Object.create(Parent.prototype)创建的原型 - 父类作为工具函数为子类实例添加属性
- 将子类原型的
constructor指向子类构造函数
function Parent(name) {
this.name = name
this.sayName = () => {
console.log('sayName:', this.name)
}
}
function Child(name, age) {
Parent.call(this, name)
this.age = age
this.sayAge = () => {
console.log('age:', this.age)
}
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.construcor = Child
const childObj = new Child('zhangsan', 20)
console.log(childObj)
childObj.sayName()
childObj.sayAge()
//{
// name: 'zhangsan',
// sayName: [Function (anonymous)],
// age: 20,
// sayAge: [Function (anonymous)]
// }
// sayName: zhangsan
// age: 20
7. 手写 new 操作符实现
- 创建一个新的对象
- 函数中的
this指向新创建的这个对象 - 修改对象的原型指向这个函数的原型
- 执行函数中的代码
- 返回新创建的对象(若函数的返回值是引用类型的数据,则返回这个引用类型的数据)
// 1. 创建一个新的对象
// 2. 函数中的 this 指向新创建的这个对象
// 3. 修改对象的原型指向这个函数的原型
// 4. 执行函数中的代码
// 5. 返回新创建的对象(若函数的返回值是引用类型的数据,则返回这个引用类型的数据)
function myNew(fun, ...args) {
// 箭头函数不能作为构造函数使用,利用箭头函数没有(显式)原型的特点,判断传入的函数是否为箭头函数
if(!fun.prototype) {
throw TypeError (`${fun.name} is not a constructor`)
}
// 以 fun 函数的原型作为新创建对象的原型
const newObj = Object.create(fun.prototype)
let res = fun.call(newObj, ...args)
if(fun() && (typeof res === 'object' || typeof res === 'function')) {
return fun()
} else {
return newObj
}
}
// 测试1:普通函数
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.say = function() {
console.log(this.age);
};
let p1 = myNew(Person, "lihua", 18);
console.log(p1.name);
console.log(p1);
p1.say();
// lihua
// { name: 'lihua', age: 18 }
// 18
// 测试2:箭头函数
// let foo = () => {}
// myNew(foo) // Uncaught TypeError: foo is not a constructor
9. 实现 instanceOf
核心思想:判断构造函数的原型是否出现在对象的原型链上。
注意,原始类型使用 instanceOf 进行判断的时候会返回 false
function myInstanceof(obj, fun) {
if(typeof obj !== 'object') {
return false
}
let funPrototype = fun.prototype
let objProto = Object.getPrototypeOf(obj)
while(objProto) {
if(objProto === funPrototype) {
return true
} else {
objProto = Object.getPrototypeOf(objProto)
}
}
return false
}
let obj = []
myInstanceof(obj, Array)
console.log('myInstanceof-1', myInstanceof(obj, Array)) // myInstanceof-1 true
console.log('myInstanceof-2', myInstanceof(obj, Object)) // myInstanceof-2 true
console.log('myInstanceof-3', myInstanceof(obj, Set)) // myInstanceof-3 true
// 注意,原始类型使用 instanceOf 进行判断的时候会返回false
// 例如:
console.log(null instanceof Object) // false
console.log(1 instanceof Number) // false
console.log('123' instanceof String) // false
console.log(true instanceof Boolean) // false
10. 实现 call、apply、bind
10-1. 实现 call
// call方法实现
// fun.call(thisObj, ...args)
// 核心思想:将fun函数添加为thisObj的方法,作为thisObj的方法来调用
// 简单实现:
// function myCall(thisObj, ...arg) {
// thisObj.fun = this // this指向调用myCall的对象
// return thisObj.fun() // 执行函数并返回执行结果
// }
// Function.prototype.myCall = myCall
// function foo() {
// console.log(this)
// }
// foo.call({name: 'zhangsan', age: 20})
// 细节处理:
function myCall(thisObj, ...arg) {
// 没有传入thisObj时,thisObj指向window
thisObj = thisObj || window
// 创建一个独一无二的值,防止覆盖thisObj原有的属性
fun = Symbol('fun')
thisObj[fun] = this
// 执行函数
const res = thisObj[fun](...arg)
// 删除方法
delete thisObj[fun]
return res
}
Function.prototype.myCall = myCall
function foo(colorParam, sportParam) {
this.color = colorParam
this.sport = sportParam
console.log(this)
}
foo.call({name: 'zhangsan', age: 21}, 'res', 'run')
10-2. 实现 apply
// apply 方法实现
// fun.apply(thisObj, argArr)
// 实现:
function myApply(thisObj, argArr) {
// thisObj = thisObj || window
// con
// 判断是否传入thisObj,没传入的话thisObj指向window
thisObj = thisObj || window
// 创建一个独一无二的值,防止覆盖thisObj原有方法
let fun = Symbol('fun')
thisObj[fun] = this
// 执行函数
const res = thisObj[fun](...argArr)
// 删除方法
delete thisObj[fun]
return res
}
Function.prototype.myApply = myApply
function foo(colorParam, sportParam) {
this.color = colorParam
this.sportParam = sportParam
console.log(this)
}
const thisObj = {name:'zhangsan', age:23}
foo.apply(thisObj, ['pink', 'jump'])
10-3. 实现 bind
// fun.bind(thisObj)
// 核心思想:对函数进行包装,包装时可利用call或者apply方法
// 实现:
function myBind(thisObj, ...arg) {
const fun = this
thisObj = thisObj || window
const argParam = arg
const res = function(...innerArgParam) {
// 参数合并
innerArg = [...argParam, ...innerArgParam]
if(this instanceof res) {
return new fun(...innerArg)
} else {
fun.call(thisObj, ...innerArg)
}
}
return res
}
Function.prototype.myBind = myBind
const thisObj = {
name: 'hahaha',
age: 24
}
function foo(colorParam, sportParam) {
this.color = colorParam
this.sport = sportParam
console.log(this)
}
foo.myBind(thisObj, 'skyBlue', 'swim')()
foo.myBind(thisObj, 'skyBlue111', 'swim111')()
foo.myBind(thisObj)('skyBlue222', 'swim222')
foo.myBind(thisObj, 'skyBlue111', 'swim111')('skyBlue222', 'swim222')
foo.myBind(thisObj, 'skyBlue111')( 'swim222')
let foo1 = foo.myBind(thisObj, 'skyBlue111', 'swim111')
new foo1()
11. 判断两个值是否相等
function looseEqual (a, b) {
if (a === b) return true
const isObjectA = isObject(a)
const isObjectB = isObject(b)
if (isObjectA && isObjectB) {
try {
const isArrayA = Array.isArray(a)
const isArrayB = Array.isArray(b)
if (isArrayA && isArrayB) {
return a.length === b.length && a.every((e, i) => {
return looseEqual(e, b[i]) // b 包含 a
})
} else if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime() // 单独处理 Date 类型, 时间戳应该是一样的
} else if ( 0 ) {
// 如果需要考虑其它类型, 添加 if 即可
} else if (!isArrayA && !isArrayB) {
const keysA = Object.keys(a)
const keysB = Object.keys(b)
// 先判断 key 的长度, 再判断 a 包含于 b
return keysA.length === keysB.length && keysA.every(key => {
return looseEqual(a[key], b[key])
})
} else {
/* istanbul ignore next */
return false
}
} catch (e) {
/* istanbul ignore next */
return false
}
} else if (!isObjectA && !isObjectB) {
return String(a) === String(b)
} else {
return false
}
}
function isObject (obj) {
return obj !== null && typeof obj === 'object'
}
const _toString = Object.prototype.toString
12. Promise
- 构造函数
- then方法
- catch方法
构造函数:
- 定义状态
- roslve函数
- reject函数
- 执行executor
rosolve
- 判断状态
- 修改状态
- 修改值
- 执行回调
reject
- 判断状态
- 修改状态
- 修改值
- 执行回调
then方法
- fulfilled状态处理
- rejected状态处理
- pending状态处理
- 返回promise