手动实现 call
call
是 JavaScript 中的一个方法,用于改变函数内部 this
的指向,并传入参数。以下是手动实现 call
的代码:
Function.prototype.myCall = function (context, ...args) {
// 如果没有传入context, 默认为window
context = context || window
// 创建一个唯一的符号,避免与对象其他属性冲突
let fn = Symbol('fn')
// 将当前函数赋值给context对象的临时方法
context[fn] = this
// 调用context上的临时方法并传入参数
const result = context[fn](...args)
// 删除临时方法,避免污染context
delete context[fn]
// 返回函数的执行结果
return result
}
思路:
this
指向调用myCall
的函数。- 如果没有传入
context
,默认为window
。 - 通过
Symbol
创建一个唯一的属性,来避免和其他属性冲突。 - 最后调用
context
上的这个临时方法,并删除它。
手动实现 apply
apply
与 call
类似,区别在于参数传递方式为数组。以下是手动实现 apply
的代码:
Function.prototype.myApply = function (context, args = []) {
// 如果没有传入context, 默认为globalThis(当前环境的全局对象)
context = context || globalThis
// 创建一个唯一的符号,避免与对象其他属性冲突
const fn = Symbol('fn')
// 将当前函数赋值给context对象的临时方法
context[fn] = this
// 调用context上的临时方法并传入参数数组
const result = context[fn](...args)
// 删除临时方法,避免污染context
delete context[fn]
// 返回函数的执行结果
return result
}
思路:
- 通过
Symbol
创建一个唯一属性,临时改变this
的指向,调用函数并传入参数数组。
手动实现 bind
bind
返回一个新的函数,新的函数可以在调用时指定 this
的指向,并且支持链式调用。以下是手动实现 bind
的代码:
Function.prototype.myBind = function (context, ...args) {
// 保留原函数的引用
const that = this
// 创建一个新的函数
const fnC = function (...argsC) {
// 判断新函数是否是通过new调用的(实例化了)
return that.apply(this instanceof fnC ? this : context, [...args, ...argsC])
}
// 继承原函数的原型链
fnC.prototype = Object.create(this.prototype)
// 返回新的函数
return fnC
}
思路:
bind
返回一个新函数,保证能够在调用时重新指定this
。- 利用
apply
来确保参数合并,并支持链式调用。
手动实现 new
手动实现 new
的过程如下:
function myNew(constructor, ...args) {
// 1. 创建一个空对象
const obj = {}
// 2. 空对象的原型指向构造函数的原型
obj.__proto__ = constructor.prototype
// 3. 执行构造函数,并将新对象作为this传入
const result = constructor.apply(obj, args)
// 4. 如果构造函数返回的是对象,则返回该对象;否则,返回新创建的对象
return result instanceof Object ? result : obj
}
思路:
- 创建一个空对象。
- 将空对象的
__proto__
指向构造函数的prototype
。 - 调用构造函数,保证
this
指向新创建的对象。 - 如果构造函数返回的是对象,则返回该对象;否则,返回创建的空对象。
实现一个简易 Promise
以下是一个简易实现的 Promise
类,支持链式调用并且遵循 Promise/A+
规范:
class NewPromise {
// 根据Promise/A+规范 定义状态值
static PENDING = '待定'
static FULFILLED = '解决'
static REJECT = '拒绝'
constructor(fn) {
// 定义结果值和初始状态
this.result = null
this.status = NewPromise.PENDING
// 创建成功/失败回调数组
this.resolveCallback = []
this.rejectCallback = []
// 执行传入的函数并传入resolve和reject
fn(this.resolve, this.reject)
}
// 定义成功方法
resolve = result => {
// 使用setTimeout确保resolve在事件循环末尾执行
setTimeout(() => {
// 状态不可逆,只有PENDING状态才能继续执行
if (this.status !== NewPromise.PENDING) return
this.status = NewPromise.FULFILLED
this.result = result
// 调用所有成功回调
this.resolveCallback.forEach(callback => callback(result))
})
}
// 定义失败方法
reject = result => {
setTimeout(() => {
if (this.status !== NewPromise.PENDING) return
this.status = NewPromise.REJECT
this.result = result
// 调用所有失败回调
this.rejectCallback.forEach(callback => callback(result))
})
}
// 定义then方法,支持链式调用
then = (onResolve, onReject) => {
// 如果then的参数不是function,则使用默认函数
typeof onResolve !== 'function' ? (onResolve = value => value) : null
typeof onReject !== 'function'
? (onReject = reason => {
throw new Error(reason instanceof Error ? reason.message : reason)
})
: null
// 返回新的Promise对象
return new NewPromise((resolve, reject) => {
// 处理成功的回调
const resolveFn = res => {
try {
const x = onResolve(res)
// 如果返回值是Promise,继续链式调用
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
// 处理失败的回调
const rejectFn = err => {
try {
const x = onReject(err)
// 如果返回值是Promise,继续链式调用
x instanceof NewPromise ? x.then(resolve, reject) : resolve(x)
} catch (error) {
reject(error)
}
}
// 根据状态执行对应的回调
switch (this.status) {
case NewPromise.PENDING:
// 如果是PENDING状态,保存回调
this.resolveCallback.push(resolveFn)
this.rejectCallback.push(rejectFn)
break
case NewPromise.FULFILLED:
// 如果是已解决状态,直接执行成功回调
setTimeout(() => onResolve(this.result))
break
case NewPromise.REJECT:
// 如果是已拒绝状态,直接执行失败回调
setTimeout(() => onReject(this.result))
break
}
})
}
}
思路:
Promise
支持链式调用,采用then
方法处理成功和失败的回调。- 通过
setTimeout
确保resolve
和reject
在事件循环末尾执行。 - 支持
PENDING
、FULFILLED
、REJECT
三个状态的转换。
手动实现深拷贝
以下是手动实现深拷贝的代码,使用 WeakMap
来处理循环引用:
function deepClone(obj, map = new WeakMap()) {
// 如果不是对象,则直接返回原值
if (typeof obj !== 'object') return obj
// 如果WeakMap中已记录该对象,说明是循环引用,直接返回对应的克隆结果
if (map.has(obj)) return map.get(obj)
// 判断是数组还是对象
const isArray = Array.isArray(obj) || Object.prototype.toString(obj) === '[object Array]'
// 创建目标对象或数组
const result = isArray ? [] : {}
// 将当前对象和对应的克隆对象加入WeakMap
map.set(obj, result)
// 如果是数组,则迭代数组;如果是对象,则迭代对象的所有属性
const iteratorArray = isArray ? obj : Object.keys(obj)
iteratorArray.forEach((val, key) => {
// 如果是对象,key为属性名;如果是数组,key为索引
if (!isArray) key = val
// 递归深拷贝
result[key] = deepClone(obj[key], map)
})
return result
}
思路:
- 使用
WeakMap
来避免循环引用导致的死循环。 - 使用递归来拷贝对象和数组,确保深拷贝。