手写系列
1. call 的实现
function call(context, ...args) {
context = context || window
let fn = Symbol('_fn')
context[fn] = this
let result = context[fn](...args)
delete context[fn]
return result
}
2. apply 的实现
function apply(context, args) {
context = context || window
let fn = Symbol('_fn')
context[fn] = this
let result = context[fn](...args)
delete context[fn]
return result
}
3. bind 的实现
function bind(context, ...args) {
let that = this
return function newFn(...newFnArgs) {
if (this instanceof newFn) {
return newFn(...args, ...newFnArgs)
}
return that.apply(context, [...args, ...newFnArgs])
}
}
4. 函数柯里化
只传递函数一个部分参数来调用它,让他它返回一个函数去处理剩余的参数
function curry(func) {
let curried = (...args) => {
if (...args.length < func.length) {
return (...reset) => curried(...args, ...reset)
}
return func(...args)
}
return curried
}
5. compose 组合函数
如果一个函数需要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并一个函数
// redux 中 compose实现
function compose(...fns) {
return fns.reduce(
(a, b) => (...args) => a(b(...args))
)
}
// 理解版
function compost2(...fns) {
return function (args) {
for (let i = fns.length - 1; i >= 0; i--) {
args = fns[i](args)
}
return args
}
}
function add1(str) {
return '1->' + str
}
function add2(str) {
return '2->' + str
}
function add3(str) {
return '3->' + str
}
// 将compose的参数 从后往前一次执行,参数为传入上一个的结果
function compose(...fns) {
return function (args) {
// fns是一个数组[add3, add2, add1] 倒序遍历执行函数得到结果传递给下一个函数
for (let i = fns.length - 1; i >= 0; i--) {
args = fns[i](args)
}
return args
}
}
let r = add3(add2(add1('地球'))) // 3->2->1->地球
let fn = compose(add3, add2, add1)
let result = fn('地球') // 3->2->1->地球
6. new 的实现
function _new(Fn, ...args) {
let obj = {}
obj.__proto__ = Fn.prototype
let result = Fn.apply(obj, args)
return result instanceof Object ? result : obj
}
function _new(Fn, ...args) {
// 创建一个新对象
let obj = {}
// 新对象的__proto__ = 构造函数的prototype
obj.__proto__ = Fn.prototype
// 执行构造函数 this执行新对象
let result = Fn.apply(obj, args)
// 构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象。
return result instanceof Object ? result : obj
}
function Parent(name) {
this.name = name
this.foot = 'rice'
}
Parent.prototype.eat = function () {
return `${this.name}+++eat+++${this.foot}`
}
Parent.prototype.setFoot = function (val) {
this.foot = val || 'noodles'
}
let p1 = _new(Parent, 'p1')
let p2 = _new(Parent, 'p2')
let p3 = _new(Parent, 'p3')
console.log(p1.name, p1.eat()) // p1 p1+++eat+++rice
p1.setFoot()
console.log(p1.name, p1.eat()) // p1 p1+++eat+++noodles
console.log(p2.name, p2.eat()) // p2 p2+++eat+++rice
console.log(p3.name, p3.eat()) // p3 p3+++eat+++rice
7. instanceof 的实现
function instanceof(left, right) {
if (typeof left !== 'object' || left === null) return false
let proto = Object.getPrototypeOf(left)
while (true) {
if (proto === null) return false
if (proto === right.prototype) return true
proto = Object.getPrototypeOf(proto);
}
}
function cInstanceof(left, right) {
// 判断左侧是否为引用类型 不是则返回false
if (typeof left !== 'object' || left === null) return false
// 获取左侧的构造函数的原型对象
let proto = Object.getPrototypeOf(left)
// 迭代执行
while (true) {
// 如果是null 则返回false
if (proto === null) return false
// 如果左侧的原型对象 等于 右侧的原型对象 说明是同一个类型
if (proto === right.prototype) return true
// 都不满足 继续获取原型对象上的原型对象
proto = Object.getPrototypeOf(proto)
}
}
console.log(cInstanceof(1, Array)) // false
console.log(cInstanceof([], Array)) // true
console.log(cInstanceof({}, Array)) // false
8. 寄生式组合继承
function Parent(name) {
this.name = name
}
function Child() {
Parent.call(this, 'Child')
this.age = 18
}
Child.prototype = Object.create(Parent.prototype)
Child.prototype.constructor = Child
function People(name, age) {
this.name = name
this.age = age
}
People.prototype.getName = function () {
return this.name
}
function Girl(name, age, sex) {
People.apply(this, arguments)
this.sex = sex
}
Girl.prototype = Object.create(People.prototype)
Girl.prototype.constructor = Girl
Girl.prototype.getSex = function () {
return this.sex
}
let girl = new Girl('lisa', 18, 'female')
console.log(girl.getName()) // lisa
console.log(girl.getSex()) // female
9. Object.is()
Object.is()
方法判断两个值是否为同一个值 传送门
提示
Object.is()
与 ==
不同。==
运算符在判断相等前对两边的变量(如果它们不是同一类型)进行强制转换,而 Object.is
不会强制转换两边的值。
Object.is()
与 ===
也不相同。差别是它们对待有符号的零和 NaN
不同,例如,===
运算符(也包括 ==
运算符)将数字 -0
和 +0
视为相等,而将 Number.NaN
与 NaN
视为不相等。
function is(x, y) {
if (x === y) {
// 针对+0 不等于 -0的情况
return x !== 0 || 1 / x === 1 / y
} else {
// 针对NaN的情况
return x !== x && y !== y
}
}
9. Object.create()
于创建一个新对象,使用现有的对象来作为新创建对象的原型prototype
function create(obj) {
function C() {}
C.prototype = obj
return new C()
}
10. Object.assign()
用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。(浅拷贝)
function assign(target, ...source) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object')
}
let ret = Object(target)
source.forEach(function (obj) {
if (obj != null) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
ret[key] = obj[key]
}
}
}
})
return ret
}
11. 实现深拷贝
function deepClone(obj, hash = new WeakMap()) {
if (typeof obj === null) return null
if (typeof obj !== 'object') return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
if (hash.has(obj)) {
return hash.get(obj)
}
let newObj = new obj.constructor()
hash.set(obj, newObj)
for (const key in obj) {
if (Object.hasOwnProperty.call(obj, key)) {
newObj[key] = deepClone(obj[key], hash)
}
}
return newObj
}
const originalObject = {
name: 'John',
age: 30,
address: {
city: 'New York',
country: 'USA',
},
hobbies: ['reading', 'traveling'],
}
const clonedObject = structuredClone(originalObject)
console.log(clonedObject)
/**
* 实现思路
* 1. null undefined 直接返回
* 2. 基本数据类型直接返回
* 3. 正则、日期需要new
* 4. 引用类型在递归处理
* 嵌套递归处理
* 1. 每次保存值 在赋值的时候直接进行判断是否存在,如果存在直接返回
* 2. 可以用weakMap 键值对映射
*/
let obj = {
name: 'diqiu',
fn: () => console.log(12),
age: [1, 2, 3, 4],
list: [
{
name: 'a',
},
{
name: 'b',
},
],
}
let copy = deepClone(obj)
obj.age[0] = 10000
console.log(obj)
console.log(copy)
const a = {
name: 'diqiu',
b: {},
}
a.b.a = a.b
let copy1 = deepClone(a)
console.log(copy1)
12. 防抖&节流
防抖概念:
- 事件触发时并不会立即执行回调,而是会等待一段时间
- 如果在等待期间再次触发事件,会继续等待
- 只有在等待期间无新的事件触发才会执行回调
应用场景:
- 输入框搜索
- 按钮的重复点击
- 上拉滚动加载
- 用户缩放事件
const debounce = (fn, waiting = 100, immediate) => {
let timer = null
let immediateFlag = false
return (...args) => {
if (timer) {
cleatTimeout(timer)
timer = null
}
if (immediate && !immediateFlag) {
fn.call(this, ...args)
immediateFlag = true
}
timer = setTimeout(() => {
fn.call(this, ...args)
}, waiting)
}
}
节流概念:
- 事件触发时会执行回调
- 事件频繁触发,则会按照间断时间去触发回调
- 不管在间断时间内多次触发事件,回调函数的执行是固定的
应用场景:
- 下拉刷新
- 鼠标移动
- 拖拽组件
const throttle = (fn, waiting, options = { leading: true }) => {
const { leading } = options
let lastTime = 0
return (...args) => {
const currentTime = new Date().getTime()
if (lastTime === 0 && !leading) {
lastTime = currentTime
}
if (currentTime - lastTime >= waiting) {
fn.call(this, ...args)
lastTime = currentTime
}
}
}
13. 实现 Promise
const STATUS = {
PENDING: 'pending',
FULFILLED: 'fulfilled',
REJECTED: 'rejected',
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) return reject(new TypeError('出错了'))
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called = false // 表示没调用过成功和失败
try {
let then = x.then
if (typeof then === 'function') {
then.call(
x,
(y) => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} else {
resolve(x)
}
} catch (error) {
if (called) return
called = true
reject(error)
}
} else {
resolve(x)
}
}
class Promise {
constructor(executer) {
this.status = STATUS.PENDING
this.value = undefined
this.reason = undefined
this.onResolveCallbacks = []
this.onRejectCallbacks = []
const resolve = (val) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.FULFILLED
this.value = val
this.onResolveCallbacks.forEach((fn) => fn())
}
}
const reject = (reason) => {
if (this.status === STATUS.PENDING) {
this.status = STATUS.REJECTED
this.reason = reason
this.onRejectCallbacks.forEach((fn) => fn())
}
}
try {
executer(resolve, reject)
} catch (error) {
reject(error)
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (val) => val
onRejected =
typeof onRejected === 'function'
? onRejected
: (err) => {
throw err
}
let p = new Promise((resolve, reject) => {
if (this.status === STATUS.FULFILLED) {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(p, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === STATUS.REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(p, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
}
if (this.status === STATUS.PENDING) {
this.onResolveCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(p, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.onRejectCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(p, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return p
}
catch(errFn) {
return this.then(null, errFn)
}
}
Promise.deferred = function () {
var dfd = {}
dfd.promise = new Promise(function (resolve, reject) {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
// promises-aplus-tests promise.js
14. 实现 Promise.all()
将多个 Promise 实例,包装成一个 Promise 实例。最终返回的状态由入参的结果决定
- 当入参的 Promise 的状态都是成功态,则返回一个数组包含每个入参的返回值(顺序与入参一致)
- 当入参的 Promise 有一个是失败态,则返回失败态的返回值
function isPromise(x) {
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
if (typeof x.then === 'function') {
return true
}
}
return false
}
Promise.all = function (values) {
return new Promise((resolve, reject) => {
let arr = []
let times = 0
function collectResult(val, key) {
arr[key] = val
if (++times === values.length) {
resolve(arr)
}
}
values.forEach((val, index) => {
if (isPromise(val)) {
val.then((y) => {
collectResult(y, index)
}, reject)
} else {
collectResult(val, index)
}
})
})
}
15. 实现 Promise.race()
将多个 Promise 实例,包装成一个 Promise 实例。返回值由最快改变状态的 Promise 来决定。
function isPromise(x) {
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
if (typeof x.then === 'function') {
return true
}
}
return false
}
Promise.race = function (values) {
return new Promise((resolve, reject) => {
values.forEach((val) => {
if (isPromise(val)) {
val.then(resolve, reject)
} else {
resolve(val)
}
})
})
}
16. 实现 Ajax
function ajax(url, methods, body, headers) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open(methods, url)
for (let key in headers) {
xhr.setRequestHeader(key, headers[key])
}
xhr.onreadystatechange = function () {
if (xhr.readyState !== 4) return
if (xhr.status === 200 || xhr.status === 304) {
resolve(xhr.responseText)
} else {
reject(new Error(xhr.responseText))
}
}
xhr.send()
})
}
17.并发请求限制
实现一个任务调度器,控制并发任务的数量,确保在任意时刻运行的任务数不超过指定的并发数量。在任务完成后,会自动开始队列中的下一个任务。
class TaskScheduler {
constructor(maxConcurrency) {
this.maxConcurrency = maxConcurrency; // 最大并发数
this.currentRunning = 0; // 当前运行的任务数
this.queue = []; // 任务队列
}
// 添加任务到队列中,并返回一个 Promise,以便外部调用者可以等待任务完成
add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject }); // 将任务及其 resolve/reject 函数添加到队列中
this.runNext(); // 尝试运行下一个任务
});
}
// 运行下一个任务
runNext() {
// 如果当前运行的任务数已达到最大并发数或队列为空,则不进行操作
if (this.currentRunning >= this.maxConcurrency || this.queue.length === 0) {
return;
}
const { task, resolve, reject } = this.queue.shift(); // 从队列中取出下一个任务
this.currentRunning++; // 增加当前运行的任务数
// 执行任务,并在任务完成后处理
task().then(resolve).catch(reject).finally(() => {
this.currentRunning--; // 任务完成后,减少当前运行的任务数
this.runNext(); // 尝试运行队列中的下一个任务
});
}
}
// 使用示例
const scheduler = new TaskScheduler(3);
function createTask(id, duration) {
return () => new Promise((resolve) => {
setTimeout(() => {
resolve(id); // 任务完成,调用 resolve
}, duration);
});
}
// 添加任务,并在任务完成后处理其他逻辑
scheduler.add(createTask(1, 2000)).then(result => {
console.log(`Result: ${result}`);
});
scheduler.add(createTask(2, 1000)).then(result => {
console.log(`Result: ${result}`);
});
scheduler.add(createTask(3, 1500)).then(result => {
console.log(`Result: ${result}`);
});
scheduler.add(createTask(4, 500)).then(result => {
console.log(`Result: ${result}`);
});
scheduler.add(createTask(5, 3000)).then(result => {
console.log(`Result: ${result}`);
});