Appearance
手写题
问题 1:实现一个函数柯里化 add
- add(1,2,3).valueOf() // 6
- add(1,2)(3)(4,5).valueOf() // 15
- add(1)(2)(3)(4,5,6)(7).valueOf() // 28
js
const curry = (...args1) => {
let params = args1
const addFn = (...args2) => {
params = params.concat(args2)
return addFn
}
addFn.valueOf = () => {
return params.reduce((pre, cur) => {
return pre + cur
}, 0)
}
return addFn
}
const add = curry(1, 2, 3)
console.log(add(4, 5, 6)(7).valueOf()) // 28
问题 2:实现响应式数据 + 依赖收集
要求:编写 constructor
,让其实现数据修改触发 render,且同步变更需要合并。
js
class Component {
data = { name: '' }
constructor() {}
render() {
console.log(`render - name:${this.proxyData.name}`)
}
}
// 要求以下代码需要触发render, 且同步变更需要合并。
const com = new Component()
com.proxyData.name = 'a'
com.proxyData.name = 'b'
com.proxyData.name = 'wifi'
// 上面三个执行完后,第一次触发 render
setTimeout(() => {
com.proxyData.name = 'hello wifi'
})
// 定时器执行完后,第二次触发 render
解析:通过 Proxy
触发响应式,加上 Promise
实现依赖收集。
js
class Component {
data = { name: '' }
pending = false
constructor() {
this.proxyData = new Proxy(this.data, {
set: (target, key, value) => {
// target => sthis.data
target[key] = value
if (!this.pending) {
this.pending = true
// 第一次和第二次触发,相差一个微任务,只需要在微任务中重新执行render就行
Promise.resolve().then(() => {
this.render()
})
}
}
})
}
render() {
this.pending = false
console.log(`render - name:${this.proxyData.name}`)
}
}
// 要求以下代码需要触发render, 且同步变更需要合并。
const com = new Component()
com.proxyData.name = 'a'
com.proxyData.name = 'b'
com.proxyData.name = 'wifi'
// 上面三个执行完后,第一次触发 render
setTimeout(() => {
com.proxyData.name = 'hello wifi'
})
// 定时器执行完后,第二次触发 render
问题 3:手写 intanceof 操作符
instanceof
运算符用于检测构造函数的 prototype
属性是否出现在某个实例对象的原型链上。
js
/**
* 思路:
* 1、通过 Object.getPrototypeOf 获取 obj 的原型
* 2、循环判断 objProtoType 是否和 constructor 的原型相等
* 2.1、如果相等就返回 true
* 2.2、如果不相等 就重新赋值一下 obj 的原型 进入下一次循环
* 3、判断是 objProtoType 是否为空 如果为空就说明不存在 返回 false
*/
function myInstanceof(obj, constructor) {
// 检查obj是否为对象或函数
if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
return false
}
// 获取构造函数的原型对象 let proto = obj.__proto__
let proto = Object.getPrototypeOf(obj)
// 检查构造函数的原型链是否包含给定的构造函数的原型
while (proto !== null) {
if (proto === constructor.prototype) {
return true
}
// obj.__proto__.__proto__ ...
proto = Object.getPrototypeOf(proto)
}
return false
}
class A {}
class B extends A {}
class C extends B {}
let obj = new C()
console.log(myInstanceof(obj, A))
问题 4:手写 Object.create
Object.create()
:创建一个新的对象,将传入的对象原型指向新对象并返回。
js
function MyObjectCreate(obj) {
function Fn() {}
Fn.prototype = obj
return new Fn()
}
class A {}
const aObj = MyObjectCreate(A.prototype)
问题 5:使用 setTimeout 实现 setInterval
js
function MySetInterval(callback, delay) {
const interval = () => {
callback()
setTimeout(interval, delay)
}
setTimeout(interval, delay)
}
MySetInterval(() => {
console.log(111)
}, 1000)
js
function MySetInterval(callback, delay) {
let timerId = null
const interval = () => {
callback()
timerId = setTimeout(interval, delay)
}
timerId = setTimeout(interval, delay)
return {
clear() {
clearTimeout(timerId)
}
}
}
const { clear } = MySetInterval(() => {
console.log(111)
}, 1000)
问题 6:发布订阅模式
js
class EventEmitter {
constructor() {
this.emit = {}
}
// 订阅事件
on(eventName, callback) {
if (!this.emit[eventName]) {
this.emit[eventName] = []
}
this.emit[eventName].push(callback)
}
// 发布事件
emits(eventName, ...args) {
if (!this.emit[eventName]) {
return new Error(eventName + 'event is not find')
}
this.emit[eventName].forEach((cb) => cb(...args))
}
// 取消订阅
off(eventName, callback) {
if (!this.emit[eventName]) {
return new Error(eventName + 'event is not find')
}
if (callback) {
this.emit[eventName] = this.emit[eventName].filter(
(cb) => cb !== callback
)
} else {
delete this.emit[eventName]
}
}
}
// 使用示例
const emitter = new EventEmitter()
// 订阅事件
emitter.on('message', (message) => {
console.log(`收到消息: ${message}`)
})
// 发布事件
emitter.emits('message', '你好,世界!')
emitter.emits('message', '你好,世界2!')
// 取消订阅事件
emitter.off('message')
// 再次发布事件,此时回调函数不会被执行
emitter.emits('message', '再见,世界!')
js
const emitter = (function () {
var deps = {}
return {
on: function (type, cb) {
deps[type] = deps[type] || []
deps[type].push(cb)
},
emit: function (type, ...rest) {
deps[type] instanceof Array &&
deps[type].forEach((cb) => cb.apply(null, rest))
},
off: function (type, cb) {
if (!deps[type]) return
let index = deps[type].findIndex((item) => item === cb)
if (index !== -1) {
deps[type].splice(index, 1)
}
}
}
})()
const handle1 = (data) => {
console.log('test 1', data)
}
emitter.on('test', handle1)
emitter.on('test', (data) => {
console.log('test 2', data)
})
setTimeout(() => {
emitter.emit('test', 'hello world')
emitter.off('test', handle1) // 取消订阅
emitter.emit('test', 'hello world') // 当 off 了handle1 后,handle1 不会再执行了
}, 1000)
问题 7:手写 节流、防抖、深浅拷贝、call、apply、bind、promise
这些问题在对应 JS 部分 和 Promise 部分有。