实现浅克隆和深克隆
/**
* 浅克隆
* @param {*} val
*/
function clone(val) {
if (typeof val !== 'object') return val;
return {
...val
};
}
/**
* 深克隆
* 1. Date 和 RegExp需要特殊处理
* 2. 循环引用也需要复制过去
*
* @param {*} val
*/
function deepClone(val, map = new WeakMap()) {
if (val instanceof RegExp) return new RegExp(val);
if (val instanceof Date) return new Date(val);
if (val === null || typeof val !== 'object') return val;
if (map.has(val)) return map.get(val);
const newObj = new val.constructor();
map.set(val, newObj);
for (const key in val) {
if (val.hasOwnProperty(key)) {
newObj[key] = deepClone(val[key], map)
}
}
return newObj;
}
module.exports = {
clone,
deepClone
}
JEST:
const {
clone,
deepClone
} = require('../clone')
test("浅克隆", () => {
const obj = {
a: {
b: 12
},
c: 23
};
const obj1 = clone(obj);
expect(obj1 === obj).toBe(false);
expect(obj1.a === obj.a).toEqual(true);
obj.a.b = 888;
expect(obj1.a.b).toBe(888)
})
test("深克隆", () => {
const obj = {
a: {
b: 12
},
c: 23
};
const obj1 = deepClone(obj);
expect(obj1 === obj).toBe(false);
expect(obj1.a === obj.a).toEqual(false);
obj.a.b = 888;
expect(obj1.a.b).toBe(12)
})
test("测试复制循环引用", () => {
const a = {
aKey: 'aaaaa'
}
const b = {
bKey: 'bbbbb'
}
a.bObj = b;
b.aObj = a;
const obj = {
a,
b
};
const obj1 = deepClone(obj);
expect(obj1.a.bObj).toBe(obj1.b);
expect(obj1.b.aObj).toEqual(obj1.a);
})
实现一个能将函数柯里化的函数
/**
* 返回一个柯里化的函数
* @param {*} fn
* @param {*} args
*/
function curry(fn) {
const args = arguments[1] || []
return function () {
const newArgs = args.concat([...arguments])
if (fn.length > newArgs.length) {
return curry.call(this, fn, newArgs)
} else {
return fn.apply(this, newArgs);
}
}
}
module.exports = {
curry
}
JEST:
const {
curry
} = require('../curry');
const {
expect
} = require('@jest/globals');
test("currt 柯里化", () => {
function add(a, b, c) {
return a + b + c;
}
const curryAdd = curry(add);
expect(curryAdd(1)(2, 3)).toBe(add(1, 2, 3))
expect(curryAdd(1, 2)(3)).toBe(add(1, 2, 3))
expect(curryAdd(1)(2)(3)).toBe(add(1, 2, 3))
})
实现 new
关键字
/**
* 实现new 关键字
* @param {*} constructor
* @param {...any} args
*/
function newObj(constructor, ...args) {
const obj = {};
Object.setPrototypeOf(obj, constructor.prototype);
const res = constructor.apply(obj, args);
return typeof res === 'object' ? res : obj;
}
JEST:
test("测试自己实现的new", () => {
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.getInfo = function () {
return this.name + this.age;
}
const person = new Person('hello', 18);
const person1 = newObj(Person, 'hello', 18);
expect(person1.name).toEqual(person.name)
expect(person1.age).toBe(person.age)
expect(person1.getInfo()).toEqual(person.getInfo())
})
实现 JSON.stringify
和 JSON.parse
const supportTypes = ['number', 'boolean', 'string', 'null', 'object']
/**
* 实现JSON.stringify
* @param {*} obj
*/
function stringify(obj) {
const type = typeof obj;
if (!supportTypes.includes(type)) return undefined;
if (type === 'string') return `\"${obj}\"`;
if (type !== 'object' || obj === null) return `${obj}`;
if (Array.isArray(obj)) {
const arr = obj.map((val) => supportTypes.includes(typeof val) ? stringify(val) : 'null')
return `[${arr.toString()}]`
}
const res = []
for (const key in obj) {
const valueType = typeof obj[key];
if (!supportTypes.includes(valueType)) continue;
res.push(`"${key}":${stringify(obj[key])}`)
}
return `{${res.join(',')}}`
}
/**
* 实现JSON.parse
* @param {s} string
*/
function parse(string) {
const fn = new Function(`return ${string}`);
return fn();
}
module.exports = {
stringify,
parse
}
JEST TEST:
const {
stringify,
parse
} = require('../json')
test('测试stringify', () => {
expect(stringify(12)).toEqual(JSON.stringify(12))
expect(stringify(true)).toEqual(JSON.stringify(true))
expect(stringify(false)).toEqual(JSON.stringify(false))
expect(stringify(null)).toEqual(JSON.stringify(null))
expect(stringify(undefined)).toEqual(JSON.stringify(undefined))
expect(stringify("1231")).toEqual(JSON.stringify("1231"))
function fn() {}
expect(stringify(fn)).toEqual(JSON.stringify(fn))
class Cls {}
expect(stringify(Cls)).toEqual(JSON.stringify(Cls))
const sym = Symbol('sym')
expect(stringify(sym)).toEqual(JSON.stringify(sym))
const arr = [null, undefined, Symbol(), 12, 34, {
a: 12
}, {
b: true,
[Symbol()]: 999,
abc: [12, 23]
}]
expect(stringify(arr)).toEqual(JSON.stringify(arr))
const obj = {
number: 12,
digit: 3.14159,
boolean: true,
boolean1: false,
a: undefined,
b: "1231",
[Symbol()]: 'hell',
arr,
fn: function () {},
y: Symbol(),
}
expect(stringify(obj)).toEqual(JSON.stringify(obj))
})
test('测试parse', () => {
const arr = [null, undefined, Symbol(), 12, 34, {
a: 12
}, {
b: true,
[Symbol()]: 999,
abc: [12, 23]
}]
const arrStr = JSON.stringify(arr);
expect(parse(arrStr)).toEqual(JSON.parse(arrStr))
const obj = {
number: 12,
digit: 3.14159,
boolean: true,
boolean1: false,
a: undefined,
b: "1231",
[Symbol()]: 'hell',
arr,
fn: function () {},
y: Symbol(),
}
const objStr = JSON.stringify(obj)
expect(parse(objStr)).toEqual(JSON.parse(objStr))
})
实现instanceof
/**
* 实现 instanceOf
* @param {*} obj
* @param {*} constructor
*/
function instanceOf(obj, constructor) {
if (!constructor || !obj) return false;
let prototype = Object.getPrototypeOf(obj);
const constructorProto = constructor.prototype;
while (true) {
if (prototype === constructorProto) return true;
if (!prototype) return false;
prototype = Object.getPrototypeOf(prototype);
}
}
JEST TEST:
test("测试实现的instanceOf", () => {
const obj = {};
expect(instanceOf(obj, Object)).toBe(obj instanceof Object)
const arr = []
expect(instanceOf(arr, Array)).toBe(arr instanceof Array)
expect(instanceOf(arr, Object)).toBe(arr instanceof Object)
const reg = new RegExp();
expect(instanceOf(reg, RegExp)).toBe(reg instanceof RegExp)
expect(instanceOf(reg, Object)).toBe(reg instanceof Object)
})
实现 Array.prototype.reduce
/**
* 实现reduce
* @param {*} arr
* @param {*} callback
* @param {*} initialVal
*/
function reduce(arr, callback, initialVal) {
for (let i = 0; i < arr.length; i++) {
initialVal = callback(initialVal, arr[i], i, arr);
}
return initialVal;
}
const arr = [1, 2, 4, 5];
console.log(reduce(arr, (res, val) => res + val, 1)) // 13
实现debounce
防抖函数
/**
* 返回一个已防抖函数
* 该函数自上一次调用后,间隔timeout时间才会调用 func
* @param {*} func
* @param {*} timeout
*/
function debounce(func, timeout) {
let handler;
return function (...props) {
clearTimeout(handler)
handler = setTimeout(() => {
func(...props);
}, timeout)
}
}
const debounceFunc = debounce((a) => {
console.log('打印', a)
}, 500)
debounceFunc(88);
debounceFunc(99);
setTimeout(() => {
debounceFunc(100);
}, 500)
// 打印 99
// 打印 1000
实现throttle
节流函数
/**
* 返回一个已节流函数
* 该函数在 time 时间内只会调用 func 一次
* @param {*} func
* @param {*} time
*/
function throttle(func, time) {
let canRun = true;
return (...params) => {
if (!canRun) return;
canRun = false;
func(...params);
setTimeout(() => {
canRun = true;
}, time)
}
}
const throttleFunc = throttle((a, b) => {
console.log('节流:', a, b)
}, 300)
throttleFunc(1, 2)
throttleFunc(2, 3)
throttleFunc(4, 5)
setTimeout(() => {
throttleFunc(6, 7)
}, 300)
// 节流: 1 2
// 节流: 6 7
实现 Promise
class MyPromise {
constructor(func) {
this.status = 'pending'
this.result = undefined;
this.error = undefined;
this.resoledCallbacks = [];
this.rejectedCallbacks = [];
const resolve = (result) => {
this.status = 'resolved'
this.result = result;
setTimeout(() => {
this.resoledCallbacks.forEach((callback) => callback())
})
};
const reject = (error) => {
this.status = 'rejected'
this.error = error;
setTimeout(() => {
this.rejectedCallbacks.forEach((callback) => callback())
})
};
try {
func && func(resolve, reject);
} catch (err) {
this.status = 'rejected';
this.error = err;
}
}
onResolve(callback, resolve, reject) {
setTimeout(() => {
if (!callback) return resolve();
try {
const result = callback(this.result);
if (result instanceof MyPromise) {
result.then((res) => {
resolve(res);
}).catch((err) => {
reject(err);
})
} else if (result instanceof Object && result.then instanceof Function) {
result.then(res => {
resolve(res);
})
} else {
resolve(result);
}
} catch (err) {
reject(err)
}
});
}
onReject(callback, resolve, reject) {
setTimeout(() => {
if (!callback) return reject(this.error);
try {
const res = callback(this.error);
resolve(res)
} catch (err) {
reject(err);
}
})
}
then(resolveCallback, rejectedCallback) {
return new MyPromise((resolve, reject) => {
if (this.status === 'resolved') {
this.onResolve(resolveCallback, resolve, reject);
} else if (this.status === 'rejected') {
this.onReject(rejectedCallback, resolve, reject);
} else {
this.resoledCallbacks.push(() => this.onResolve(resolveCallback, resolve, reject));
this.rejectedCallbacks.push(() => this.onReject(rejectedCallback, resolve, reject));
}
})
}
catch (callback) {
return new MyPromise((resolve, reject) => {
if (this.status === 'resolved') {
resolve(this.result);
} else if (this.status === 'rejected') {
this.onReject(callback, resolve, reject);
} else {
this.resoledCallbacks.push(() => resolve(this.result));
this.rejectedCallbacks.push(() => this.onReject(callback, resolve, reject));
}
})
}
onFinally(callback, resolve, reject) {
setTimeout(() => {
try {
callback && callback();
if (this.status === 'resolved') resolve(this.result);
if (this.status === 'rejected') reject(this.error);
} catch (err) {
reject(err)
}
});
}
/**
*
* @param {*} callback
*/
finally(callback) {
return new MyPromise((resolve, reject) => {
if (this.status === 'pending') {
this.resoledCallbacks.push(() => this.onFinally(callback, resolve, reject));
this.rejectedCallbacks.push(() => this.onFinally(callback, resolve, reject));
} else {
this.onFinally(callback, resolve, reject);
}
})
}
}
module.exports = MyPromise
Jest 测试:
const MyPromise = require('./MyPromise');
// const MyPromise = Promise;
test('错误穿透', done => {
const errorVal = '错误';
new MyPromise((resolve, reject) => {
reject(errorVal)
}).then()
.catch()
.then(() => {})
.catch((err) => {
expect(err).toBe(errorVal)
done();
})
});
test('异步promise', done => {
const VAL = 888;
new MyPromise((resolve) => {
setTimeout(() => {
resolve(VAL)
}, 100)
}).then((val) => {
expect(val).toBe(VAL)
done()
})
})
test('then中返回resolved 状态 的promise', done => {
const VAL = 888;
new MyPromise((resolve) => {
resolve(VAL)
}).then((val) => {
return new MyPromise((resolve) => {
setTimeout(() => {
resolve(val);
}, 100)
})
}).then((val) => {
expect(val).toBe(VAL)
done()
})
})
test('then中返回rejected状态的promise', done => {
const VAL = 888;
new MyPromise((resolve) => {
resolve(VAL)
}).then((val) => {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(val);
}, 100)
})
}).then((val) => {
}).catch((val) => {
expect(val).toBe(VAL)
done()
})
})
test('then穿透', done => {
const VAL = 888;
new MyPromise((resolve, reject) => {
setTimeout(() => {
reject(VAL);
}, 100)
}).then()
.then((val) => {
}).catch((val) => {
expect(val).toBe(VAL)
done()
})
})
test('catch 穿透', done => {
const VAL = 888;
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(VAL);
}, 100)
}).catch((val) => {})
.catch()
.then((val) => {
expect(val).toBe(VAL)
done()
})
})
test('finally: 同步resolve', done => {
const VAL = 999;
new MyPromise((resolve, reject) => {
resolve(VAL)
}).finally((val) => {
expect(val).toBeUndefined();
}).catch((err) => {}).then((val) => {
expect(val).toBe(VAL)
done();
})
})
test('finally: 异步resolve', done => {
const VAL = 999;
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(VAL)
}, (100));
})
.finally((val) => {
expect(val).toBeUndefined();
})
.catch((err) => {})
.then((val) => {
expect(val).toBe(VAL)
done();
})
})
test('finally: 抛出错误', done => {
const VAL = '999';
new MyPromise((resolve, reject) => {
resolve()
})
.finally((val) => {
throw new Error(VAL)
})
.then((val) => {
})
.catch((err) => {
expect(err.message).toBe(VAL)
done();
})
})
实现 Promise.all
, Promise.race
, Promise.allSelected
, Promise.any
, Promise.resolve
, Promise.reject
, Promise.try
function resolve(val) {
if (val instanceof Promise) return val;
if (val && val.then instanceof Function) return new Promise(val.then);
return new Promise((resolve) => resolve(val));
}
function reject(val) {
return new Promise((resolve, reject) => reject(val))
}
function all(promises) {
promises = promises.map((promise) => resolve(promise))
const results = []
let count = 0;
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then((result) => {
results[index] = result;
count++;
if (count === promises.length) {
resolve(results);
}
}).catch((err) => {
reject(err);
})
});
})
}
function race(promises) {
let isPending = true;
return new Promise(
(resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
if (!isPending) break;
promises[i].then((result) => {
isPending = false;
resolve(result)
}).catch((err) => {
isPending = false;
reject(err)
})
}
}
)
}
function allSelected(promises) {
const results = []
let count = 0;
const check = (resolve) => {
count++;
if (count === promises.length) {
resolve(results);
}
}
return new Promise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then((result) => {
results[index] = {
status: 'fulfilled',
value: result
};
check(resolve);
}).catch((err) => {
results[index] = {
status: 'rejected',
reason: err
}
check(resolve)
})
});
})
}
function any(promises) {
const errors = []
let count = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then((result) => {
resolve(result)
}).catch((err) => {
errors[i] = err;
count++;
if (count === promises.length) {
reject(errors)
}
})
}
})
}
function myTry(callback) {
return new Promise((resolve, reject) => {
resolve(callback())
})
}
module.exports = {
resolve,
reject,
all,
race,
allSelected,
any,
try: myTry
}
JEST 测试:
const MyPromise = require('../promiseStatic');
// const MyPromise = Promise
test('Promise.reject', done => {
const p = Promise.resolve(999)
MyPromise.reject(p).catch((err) => {
expect(err).toBe(p)
done()
})
})
test('Promise all', done => {
const RESULTS = [888, 999, 1000, 1111, 2222]
const p1 = new Promise((resolve, reject) => {
resolve(RESULTS[0])
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[1]);
}, 300)
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[2])
}, (500));
})
const p4 = {
then(resolve) {
resolve(RESULTS[3])
}
}
const p5 = RESULTS[4];
MyPromise.all([p1, p2, p3, p4, p5]).then((res) => {
res.forEach((r, index) => {
expect(r).toBe(RESULTS[index])
})
done();
})
});
test('Promise race', done => {
const RESULTS = [888, 999, 1000, 1111, 2222]
MyPromise.race([
Promise.resolve(RESULTS[0]),
Promise.resolve(RESULTS[1]),
Promise.resolve(RESULTS[2]),
Promise.resolve(RESULTS[3]),
]).then((res) => {
expect(res).toBe(RESULTS[0])
done();
})
});
test('异步Promise race', done => {
const RESULTS = [888, 999, 1000]
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[0])
}, (100));
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[1])
}, (200));
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[2])
}, (300));
})
MyPromise.race([p1, p2, p3]).then((res) => {
expect(res).toBe(RESULTS[0])
done();
})
});
test('Promise race reject', done => {
const RESULTS = [888, 999, 1000]
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(RESULTS[0])
}, (100));
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[1])
}, (200));
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[2])
}, (300));
})
MyPromise.race([p1, p2, p3])
.then((res) => {})
.catch((err) => {
expect(err).toBe(RESULTS[0])
done()
})
});
test('Promise allSelected', done => {
const RESULTS = [888, 999, 1000]
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(RESULTS[0])
}, (100));
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[1])
}, (200));
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[2])
}, (300));
})
MyPromise.allSelected([p1, p2, p3])
.then(([r1, r2, r3]) => {
expect(r1.status).toBe('rejected')
expect(r1.reason).toBe(RESULTS[0])
expect(r2.status).toBe('fulfilled')
expect(r2.value).toBe(RESULTS[1])
expect(r3.status).toBe('fulfilled')
expect(r3.value).toBe(RESULTS[2])
done()
})
});
test('Promise any', done => {
const RESULTS = [888, 999, 1000]
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(RESULTS[0])
}, (100));
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[1])
}, (200));
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(RESULTS[2])
}, (300));
})
MyPromise.any([p1, p2, p3])
.then((res) => {
expect(res).toBe(RESULTS[1])
done()
})
});
test('Promise any reject', done => {
const RESULTS = [888, 999, 1000]
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(RESULTS[0])
}, (100));
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(RESULTS[1])
}, (200));
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(RESULTS[2])
}, (300));
})
MyPromise.any([p1, p2, p3])
.catch((res) => {
res.forEach((r, index) => {
expect(r).toBe(RESULTS[index])
})
done()
})
});
test('Promise try', done => {
const results = [];
const mydo = () => {
results.push(888)
}
MyPromise.try(mydo)
.then((res) => {
expect(results[0]).toBe(888)
expect(results[1]).toBe(999)
done()
})
results.push(999);
});
test('Promise try async', done => {
const results = [];
const mydo = () => {
return Promise.resolve().then(() => {
results.push(888)
return 666;
})
}
MyPromise.try(mydo)
.then((res) => {
expect(res).toBe(666)
expect(results[0]).toBe(999)
expect(results[1]).toBe(888)
done()
})
results.push(999);
});
实现 EventBus
class EventBus {
constructor() {
this.eventMap = new Map();
}
on(name, callback) {
if (this.eventMap.has(name)) {
this.eventMap.get(name).add(callback)
} else {
this.eventMap.set(name, new Set([callback]))
}
}
emit(name, ...args) {
const callbackSet = this.eventMap.get(name);
if (callbackSet) {
for (const callback of callbackSet) {
callback(...args);
}
}
}
off(name, callback) {
const callbackSet = this.eventMap.get(name);
if (!callbackSet) return false;
if (callback) {
callbackSet.delete(callback)
} else {
this.eventMap.delete(name)
}
}
once(name, callback) {
const handle = (...args) => {
callback(...args);
this.off(name, handle)
}
this.on(name, handle);
}
}
module.exports = EventBus;
JEST TEST:
const EventBus = require('../EventBus');
test('on emit', done => {
const eventBus = new EventBus();
const name = 'hello'
const ARGS = [1, 2]
eventBus.on(name, (...args) => {
ARGS.forEach((val, index) => {
expect(args[index]).toBe(val);
})
})
eventBus.on(name, (...args) => {
ARGS.forEach((val, index) => {
expect(args[index]).toBe(val);
})
})
eventBus.emit(name, ...ARGS);
done();
});
test('on off', done => {
const eventBus = new EventBus();
const name = 'hello'
const ARGS = [1, 2]
let result = []
const handle = (...args) => {
result = args;
}
eventBus.on(name, handle)
eventBus.emit(name, ...ARGS);
expect(result).toStrictEqual(ARGS);
done();
});
test('on off1', done => {
const eventBus = new EventBus();
const name = 'hello'
const ARGS = [1, 2]
let result = []
const handle = (...args) => {
result = args;
}
eventBus.on(name, handle)
eventBus.off(name, handle)
eventBus.emit(name, ...ARGS);
expect(result.length).toBe(0);
done();
});
test('once', (done) => {
const eventBus = new EventBus();
const name = 'hello'
let result = 0
const handle = () => {
result++;
}
eventBus.once(name, handle)
eventBus.emit(name);
eventBus.emit(name);
expect(result).toBe(1);
done();
})
test('once2', (done) => {
const eventBus = new EventBus();
const name = 'hello'
let result = 0
const handle = () => {
result++;
}
eventBus.on(name, handle)
eventBus.emit(name);
eventBus.emit(name);
expect(result).toBe(2);
done();
})
实现 bind
、apply
和 call
function bind(func, thisArg) {
const key = Symbol();
thisArg[key] = func;
return (...args) => thisArg[key](...args)
}
function apply(func, thisArg, argArray) {
if (!thisArg) thisArg = {};
const key = Symbol();
thisArg[key] = func;
const result = thisArg[key](...argArray)
delete(thisArg, key)
return result
}
function call(func, thisArg, ...argArray) {
return apply(func, thisArg, argArray);
}
module.exports = {
bind,
apply,
call
}
JEST TEST:
const {
bind,
apply,
call
} = require('../bind')
test('bind', done => {
function myDo() {
return this.value;
}
const obj = {
value: 888,
myDo
}
expect(obj.value).toBe(obj.myDo())
const obj1 = {
value: 999,
myDo
}
expect(obj1.value).toBe(obj1.myDo())
myDo = bind(myDo, {
value: 666
})
const obj2 = {
value: 777,
myDo
}
const obj3 = {
value: 555,
myDo
}
expect(obj2.myDo()).toBe(666)
expect(obj3.myDo()).toBe(666)
done()
});
test('apply', done => {
function myDo(a, b) {
return this.value + a + b;
}
const obj = {
value: 1
}
expect(apply(myDo, obj, [2, 3])).toBe(6)
expect(apply(myDo, null, [2, 3])).toBe(NaN)
expect(call(myDo, {
value: 2
}, 2, 3)).toBe(7)
done()
});
实现一个scheduler
调度器
说明: 调度器可以加入多个任务,但只能同时执行指定数目的。
class Scheduler {
constructor(num = 2) {
this.limitNum = num;
this.tasks = [];
this.runNum = 0;
}
async run() {
if (!this.tasks.length) {
this.runNum = 0;
return;
}
const num = this.limitNum - this.runNum
const runTasks = this.tasks.splice(0, num);
this.runNum += num
await Promise.all(runTasks.map((task) => task()))
this.run();
}
add(fn, ...args) {
return new Promise((resolve, reject) => {
const task = async () => {
try {
const res = await fn(...args)
resolve(res)
} catch (e) {
reject(e);
} finally {
if (this.runNum > 0) {
this.runNum--;
}
}
}
this.tasks.push(task);
if (this.runNum < this.limitNum) {
this.run();
}
})
}
}
const scheduler = new Scheduler();
const timeout = (fn, time = 500) => new Promise((resolve) => {
setTimeout(() => {
resolve(fn())
}, time)
})
scheduler.add(() => {
console.log('sync-1')
}).then(() => {
console.log('sync-1-end')
})
scheduler.add(() => {
return timeout(() => {
console.log('async-1')
})
}).then(() => {
console.log('async-1-end')
})
scheduler.add(() => {
return timeout(() => {
console.log('async-2')
})
}).then(() => {
console.log('async-1-end')
})
scheduler.add(() => {
return timeout(() => {
console.log('async-3')
})
}).then(() => {
console.log('async-2-end')
})
scheduler.add(() => {
return timeout(() => {
console.log('async-4')
})
}).then(() => {
console.log('async-3-end')
})
scheduler.add(() => {
console.log('sync-2')
}).then(() => {
console.log('sync-2-end')
})
// sync-1
// sync-1-end
// async-1
// async-1-end
// async-2
// async-1-end
// async-3
// async-2-end
// async-4
// async-3-end
// sync-2
实现一个事件委托
<ul class="container">
<li class="item">1231<span style="color: red">123131</span></li>
<li class="item">8888</li>
<li class="item">第三行</li>
<li class="item"><div>第四行</div></li>
</ul>
<script>
const container = document.querySelector(".container");
delegate(container, ".item", "click", (e, item) => {
console.log(item.textContent);
});
function delegate(container, itemSelector, event, handler) {
container.addEventListener(event, (e) => {
let target = e.target;
while (!target.matches(itemSelector) && target !== container) {
target = target.parentNode;
}
if (target !== container) {
handler(e, target);
}
});
}
</script>
使用css实现一个滑块按钮
描述: 只使用一个div实现一个滑块按钮,容器高度固定,宽度不固定,hover按钮的时候,里面的滑块由左侧滑到右侧,要求过程要有动画。
<div class="item-div"></div>
<style>
.item-div {
height: 50px;
border: 1px solid red;
position: relative;
}
.item-div::before {
content: "";
display: block;
position: absolute;
width: 50px;
height: 50px;
left: 0;
background-color: blue;
transition: all 0.5s ease;
}
.item-div:hover {
border-color: green;
}
.item-div:hover.item-div::before {
margin-left: 100%;
transform: translate(-100%);
}
</style>
实现一个可拖拽的div
<div class="box"></div>
<style>
.box {
position: absolute;
width: 100px;
background-color: red;
height: 100px;
left: 0;
top: 0;
}
</style>
<script>
let lastPosition = [];
const boxEle = document.querySelector(".box");
let isDraging = false;
boxEle.addEventListener("mousedown", (e) => {
isDraging = true;
lastPosition[0] = e.clientX;
lastPosition[1] = e.clientY;
});
document.addEventListener("mousemove", (e) => {
if (!isDraging) return;
const deltaX = e.clientX - lastPosition[0];
const deltaY = e.clientY - lastPosition[1];
const curLeft = parseInt(boxEle.style.left || 0);
const curTop = parseInt(boxEle.style.top || 0);
boxEle.style.cssText = `left: ${curLeft + deltaX}px;top: ${
curTop + deltaY
}px`;
lastPosition[0] = e.clientX;
lastPosition[1] = e.clientY;
});
document.addEventListener("mouseup", () => {
isDraging = false;
});
</script>
实现数组的flatDeep
函数
/**
* 拍平数组,level表示拍平的层级,
* level==1时表示仅拍平arr的子项数组
* level -1 会全部拍平
* @param {*} arr
* @param {*} level
*/
function flatDeep(arr, level = 1) {
return level !== 0 ?
arr.reduce((res, item) => Array.isArray(item) ? res.concat(flatDeep(item, level - 1)) : res.concat([item]), []) :
arr.slice();
}
const arr = [1, 2, 3, [5, 6, 7, [9, 10, 11, [12, 13, 14]]]]
console.log(flatDeep(arr))
// [ 1, 2, 3, 5, 6, 7, [ 9, 10, 11, [ 12, 13, 14 ] ] ]
console.log(flatDeep(arr, 2))
// [ 1, 2, 3, 5, 6, 7, 9, 10, 11, [ 12, 13, 14 ] ]
console.log(flatDeep(arr, -1))
// [1, 2, 3, 5, 6,7, 9, 10, 11, 12,13, 14]
实现一个能返回判断对象类型函数的函数
// 实现isType, 使
const isFunction = isType('Function');
const isNumber = isType('Number');
const isArray = isType('Array');
console.log(isNumber(12))// true
console.log(isArray([])) // true
function isType(type) {
return (val) => Object.prototype.toString.call(val) === `[object ${type}]`
}
const isFunction = isType('Function');
const isArray = isType('Array');
const isNumber = isType('Number');
console.log(isFunction(12)) // false
console.log(isFunction(() => {})) // true
console.log(isArray({})) // false
console.log(isArray([])) // true
console.log(isNumber(12)) // true
console.log(isNumber(true)) // false
实现组合继承
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
Person.prototype.getInfo = function () {
return `Name:${this.name}\nAge:${this.age}\nSex:${this.sex}`
}
function Student(name, age, sex, id) {
Person.call(this, name, age, sex)
this.id = id;
}
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.getStudentInfo = function () {
const info = this.getInfo();
return `${info}\nID:${this.id}`
}
const s1 = new Student('Per', 18, 'man', '9527');
console.log(s1.getInfo())
// Name:Per
// Age:18
// Sex:man
console.log(s1.getStudentInfo())
// Name:Per
// Age:18
// Sex:man
// ID:9527
console.log(s1 instanceof Student) // true
console.log(s1 instanceof Person) // true
手写AJAX
const xmr = new XMLHttpRequest();
xmr.open('GET', '/get/user', true);
xmr.onreadystatechange = function () {
if (xmr.readyState === 4 && xmr.status === 200) {
console.log(xmr.response);
}
}
xmr.send();
用正则实现trim
function trim(str) {
return str.replace(/^\s+|\s+$/, '');
}
console.log(' 1231231 234234 ')
console.log(trim(' 1231231 234234 '))
// 1231231 234234
// 1231231 234234
手写Object.create()
/**
* 实现obj.create
* @param {*} proto
*/
function create(proto) {
function Fn() {}
Fn.prototype = proto;
Fn.constructor = Fn;
return new Fn();
}
test("测试create", () => {
const a = {
aKey: 'aaaaa',
getVal() {
return this.name;
}
}
const obj1 = Object.create(a);
const obj2 = create(a);
obj1.aKey = 888;
expect(obj1.__proto__.aKey).toBe(obj2.__proto__.aKey);
})
进制转换
/**
* 进制转换
*/
function convert(num, scale = 10) {
if (scale === 10) return num.toString();
const numStr = '0123456789ABCDEF';
const stack = [];
while (num) {
stack.push(num % scale)
num = (num / scale) | 0
}
let res = ''
for (const val of stack) {
res = val + res;
}
return res;
}
const {
convert
} = require('../convert')
test("进制转换", () => {
const num = 888;
expect(convert(num, 2)).toBe(num.toString(2))
expect(convert(num, 3)).toBe(num.toString(3))
expect(convert(num, 4)).toBe(num.toString(4))
expect(convert(num, 5)).toBe(num.toString(5))
expect(convert(num, 6)).toBe(num.toString(6))
expect(convert(num, 7)).toBe(num.toString(7))
expect(convert(num, 8)).toBe(num.toString(8))
expect(convert(num, 9)).toBe(num.toString(9))
expect(convert(num, 10)).toBe(num.toString(10))
expect(convert(num, 11)).toBe(num.toString(11))
expect(convert(num, 12)).toBe(num.toString(12))
expect(convert(num, 16)).toBe(num.toString(16))
})
为数字字符串千分位增加分割符
function thousandth(str) {
return str.replace(/\d(?=(?:\d{3})+(?:\.\d+|$))/g, '$&,');
}
console.log(thousandth('1123123123123123.3453'))
// 1,123,123,123,123,123.3,453