# 编程实战
# 实现一个new操作符
function new(Con, ...args) {
// 创建一个空的对象
let obj = {};
// 链接到原型
Object.setPrototypeOf(obj, Con.prototype); // obj.__proto__ = Con.prototype;
// 绑定 this,执行构造函数
let result = Con.apply(obj, ...args);
// 判断构造函数返回值是否为对象,如果为对象就使用构造函数返回的值,否则使用obj
return typeof result === 'object' ? result : obj
}
# 实现一个Object.create
思路:将传入的对象作为原型
function create(obj) {
function F() {}
F.prototype = obj
return new F()
}
# 实现一个JSON.stringify
# 实现一个JSON.parse
var jsonStr = '{ "age": 20, "name": "jack" }';
var json = new Function("return " + jsonStr)();
原理:
ƒ anonymous() {
return { "age": 20, "name": "jack" }
}
# 实现一个call
方法1
Function.prototype._call1 = function (obj) {
if (typeof this !== "function") {
throw new TypeError("Error");
}
// 默认上下文是window
let context = obj || window;
// 前面讲的关键,将函数本身作为对象context的属性调用,自动绑定this
context._fn = this;
const args = [...arguments].slice(1);
const result = context._fn(...args);
// 删除 fn
delete context._fn;
return result;
};
const obj = {
age: "88",
};
function People() {
return this.age;
}
const r = People._call(obj);
console.log(r);
方法2
Function.prototype._call2 = function (context, ...args) {
if (typeof this !== "function") {
throw new TypeError("Error");
}
let fn = this;
return fn.apply(context, ...args);
};
# 实现一个apply
Function.prototype._apply = function (context, ...args) {
if (typeof this !== "function") {
throw new TypeError("Error");
}
const obj = context || window;
obj._fn = this;
let result;
if (args === undefined || args === null) {
//undefined 或者 是 null 不是 Iterator 对象,不能被 ...
result = obj._fn(args);
} else if (typeof args === "object") {
result = obj._fn(...args);
}
delete obj._fn;
return result;
};
# 实现一个bind
Function.prototype._bind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('Error');
}
var _this = this;
var args = [...arguments].slice(1);
// 返回一个函数
return function F() {
// 因为返回了一个函数,我们可以 new F(),所以需要判断
if (this instanceof F) {
return new _this(...args, ...arguments);
}
return _this.apply(context, args.concat(...arguments));
}
}
# 实现Object.assign
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
var to = Object(target);
for (var index = 1; index < arguments.length; index++) {
var nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true,
enumerable: false
});
}
more: Object.assign 方法肯定不会拷贝原型链上的属性,所以模拟实现时需要用 hasOwnProperty(..) 判断处理下,但是直接使用 myObject.hasOwnProperty(..) 是有问题的,因为有的对象可能没有连接到 Object.prototype 上(比如通过 Object.create(null) 来创建),这种情况下,使用 myObject.hasOwnProperty(..) 就会失败
# 实现一个继承
寄生组合继承
function SuperType(name) {
this.name = name;
this.colors = ['red', 'blue']
}
SuperType.prototype.sayName = function() {
console.log(this.name)
};
function SubType(name, age) {
//继承属性 //第二次调用
SuperType.call(this, name);
this.age = age;
}
//继承方法
SubType.prototype = Object.create(SuperType.prototype); //第一次调用
SubType.prototype.constructor = SubType; //如果不添加,原型(父类的实例)上是没有这个属性的
SubType.prototype.sayAge = function() {
console.log(this.age)
};
var ins3 = new SubType('yixing', 24);
ins3.colors.push('green');
console.log(ins3.colors); //'red,blue,green'
ins3.sayName(); //'yixing'
ins3.sayAge(); //24
var ins4 = new SubType('xiaoya', 23);
ins4.colors.push('yellow');
ins4.sayName(); //'xiaoya'
ins4.sayAge(); //23
# 实现一个JS函数柯里化
实现柯里化
function curry(func, args) {
var length = func.length; //获取函数的参数值
var args = args || [];
return function () {
var _args = [].slice.apply(arguments);
args.push(..._args);
if (args.length < length) {
return createCurry.call(this, func, args);
}
return func.apply(this, args);
}
}
# 实现 add 返回结果是1,2,3之和
方法1
function curry(fn) {
let arr = [];
return function f() {
if (arguments.length !== 0) {
arr.push(...Array.from(arguments));
return f;
} else {
console.log("arr", arr);
return fn(...arr);
}
};
}
function add(...args) {
//求和
return args.reduce((a, b) => a + b);
}
let sumFn = curry(add);
console.log(sumFn(1)(2)(3)(4)()); //10
方法2
function add() {
const args = [].slice.call(arguments);
let fn = function () {
const arg_fn = [].slice.call(arguments);
return add.apply(null, args.concat(arg_fn));
};
fn.toString = function () {
return args.reduce((a, b) => a + b);
};
return fn;
}
console.log(+add(1, 2, 3)); //6
console.log(+add(1)(2, 4)(3)); //10
方法3
function add(...args) {
function fn(...rest) {
args = args.concat(rest);
return fn;
}
console.log("args", args);
fn.toString = function () {
return args.reduce((a, b) => {
return a + b;
}, 0);
};
return fn;
}
console.log(+add(1)(2)(3)(4));
console.log(+add(1, 2, 3)(4, 5)(5));
# 实现Promise
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
function MyPromise(fn) {
// 保存初始化状态
var self = this;
// 初始化状态
this.state = PENDING;
// 用于保存 resolve 或者 rejected 传入的值
this.value = null;
// 用于保存 resolve 的回调函数
this.resolvedCallbacks = [];
// 用于保存 reject 的回调函数
this.rejectedCallbacks = [];
// 状态转变为 resolved 方法
function resolve(value) {
// 判断传入元素是否为 Promise 值,如果是,则状态改变必须等待前一个状态改变后再进行改变
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变,
if (self.state === PENDING) {
// 修改状态
self.state = RESOLVED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.resolvedCallbacks.forEach((callback) => {
callback(value);
});
}
}, 0);
}
// 状态转变为 rejected 方法
function reject(value) {
// 保证代码的执行顺序为本轮事件循环的末尾
setTimeout(() => {
// 只有状态为 pending 时才能转变
if (self.state === PENDING) {
// 修改状态
self.state = REJECTED;
// 设置传入的值
self.value = value;
// 执行回调函数
self.rejectedCallbacks.forEach((callback) => {
callback(value);
});
}
}, 0);
}
// 将两个方法传入函数执行
try {
fn(resolve, reject);
} catch (e) {
// 遇到错误时,捕获错误,执行 reject 函数
reject(e);
}
}
MyPromise.prototype.then = function (onResolved, onRejected) {
// 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
onResolved =
typeof onResolved === "function"
? onResolved
: function (value) {
return value;
};
onRejected =
typeof onRejected === "function"
? onRejected
: function (error) {
throw error;
};
// 如果是等待状态,则将函数加入对应列表中
if (this.state === PENDING) {
this.resolvedCallbacks.push(onResolved);
this.rejectedCallbacks.push(onRejected);
}
// 如果状态已经凝固,则直接执行对应状态的函数
if (this.state === RESOLVED) {
onResolved(this.value);
}
if (this.state === REJECTED) {
onRejected(this.value);
}
};
function timeout(time) {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
async function run() {
console.log("time start");
try {
await timeout(3000);
console.log("time end");
} catch (error) {
console.log("error", error);
}
}
run();
const p = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(123);
}, 3000);
});
console.log(p);
p.then((value) => {
console.log("value", value);
});
# 实现Promise all
_promise.prototype.all = function (promises) {
let arr = [],
count = 0;
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item)
.then((res) => {
arr[i] = res;
count += 1;
if (count === promises.length) {
resolve(arr);
}
})
.catch(reject);
});
});
};
# 实现Promise finally
_promise.prototype.finally = function (callback) {
return this.then(
(value) => {
return Promise.resolve(callback()).then(() => {
return value;
});
},
(err) => {
return Promise.resolve(callback()).then(() => {
throw err;
});
}
);
};
# 实现Promise race
_promise.prototype.race = function (promises) {
return new Promise((resolve, reject) => {
// 这里不需要使用索引,只要能循环出每一项就行
for (let i = 0; i < promises.length; i++) {
const promise = promises[i];
promise.then(resolve, reject);
}
});
};
# 实现Promise any
_promise.prototype.any = function (promises) {
let arr = [],
count = 0;
return new Promise((resolve, reject) => {
promises.forEach((item, i) => {
Promise.resolve(item).then(resolve, (err) => {
arr[i] = { status: "rejected", val: err };
count += 1;
if (count === promises.length) {
reject(new Error("没有promise成功"));
}
});
});
});
};
# 实现Promise allSettled
等所有异步操作结束了在继续执行下一步操作
_promise.prototype.allSettled = function (promises) {
let arr = [],
count = 0;
return new Promise((resolve, reject) => {
const processResult = (res, index, status) => {
arr[index] = { status: status, val: res };
count += 1;
if (count === promises.length) {
resolve(arr);
}
};
promises.forEach((item, i) => {
Promise.resolve(item).then(
(res) => {
processResult(res, i, "fulfilled");
},
(err) => {
processResult(err, i, "rejected");
}
);
});
});
};
# 实现防抖(Debounce)
思路:不空则清
function debounce(fn, wait) {
let timer = null;
return function () {
const context = this;
const args = [].slice.call(arguments);
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(function () {
fn.apply(context, args);
}, wait);
};
}
const test = debounce(fn, 3000);
立即执行:
function debounceNow(func, wait) {
let timeout = null;
return function () {
if (timeout) clearTimeout(timeout);
let callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) {
func();
}
};
}
# 实现节流(Throttle)
两种实现方法,计时器和时间
计时器:没有则定义
function throttle(fn, duration) {
var timer = null;
return function () {
const context = this;
const args = [].slice.call(arguments);
if (!timer) {
timer = setTimeout(function () {
fn.apply(context, ...args);
timer = null;
}, duration);
}
};
}
时间版本
function throttle(func, duration) {
var oldTime = new Date().getTime();
return function () {
const context = this;
const args = [].slice.call(arguments);
var newTime = new Date().getTime();
if (newTime - oldTime > duration) {
func.apply(context, args);
oldTime = newTime;
}
};
}
# 实现一个JS浅拷贝
function shallowCopy(src) {
var dst = {};
for (var prop in src) {
if (src.hasOwnProperty(prop)) {
dst[prop] = src[prop];
}
}
return dst;
}
obj.hasOwnProperty(..) 只会检查属性是否在 obj 对象中,不会检查 [[Prototype]] 原型链。
# 实现一个JS深拷贝
- JSON.parse(JSON.stringify(obj)) 【弊端:继承的属性会丢失】因为在序列化JavaScript对象时,所有函数和原型成员会被有意忽略
- 递归实现
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1);
}
function clone(target) {
//判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
// P.S 此处如果是使用Array.isArray()来判断那么必须要放在 typeof obj === 'object'之前
let result, targetType = checkedType(target);
if (targetType === 'Object') {
result = {};
} else if (targetType === 'Array') {
result = [];
} else {
return target;
}
//遍历目标数据
for (let i in target) {
//获取遍历数据结构的每一项值。
let value = target[i];
//判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
//对象/数组里嵌套了对象/数组
//继续遍历获取到value值
result[i] = clone(value);
} else {
//获取到value值是基本的数据类型或者是函数。
result[i] = value;
}
}
return result;
}
# 实现一个instanceOf
instanceof 运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置
function instanceOf(left, right) {
// 获得类型的原型
let prototype = right.prototype
// 获得对象的原型
left = left.__proto__
// 判断对象的类型是否等于类型的原型
while (true) {
if (left === null)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
# 实现一个flat方法
方法1:
function findType(data) {
return Object.prototype.toString.call(data).slice(8, -1);
}
function flatMap(arr) {
let newArray = [];
for (let i = 0; i < arr.length; i++) {
const item = arr[index];
if (Array.isArray(item)) {
newArray = newArray.concat(method(item));
} else {
newArray = newArray.concat(item);
}
}
return newArray;
}
方法2
function flatMap(arr) {
//利用ES6提供的flat方法
return arr.flat(Infinity);
}
# 实现一个 obj={a:0} 每次取属性a 值+1
const obj = {
a: 0
}
let sum = 0
Object.defineProperty(obj, 'a', {
get() {
return sum++
}
})
console.log(obj.a);
console.log(obj.a);
console.log(obj.a);
# 实现原生Ajax
// 原生XMLHttpRequest对象
let req = new XMLHttpRequest();
req.onreadystatechange = function () {
if (req.readyState === 4) {
// do something
// console.log(req.getAllResponseHeaders());
console.log(req.responseText);
}
}
req.open('GET', 'https://www.baidu.com', true);
req.setRequestHeader("Content-Type", "application/javascript;charset=UTF-8");
// req.send(null) //GET 请求发送空Body
req.send({
name: 12
})
# 实现二叉树广度优先遍历
function bfs(tree) {
const queue = [];
const value = [];
queue.push(tree);
while (queue.length !== 0) {
const item = queue.shift();
if (item.left) {
queue.push(item.left);
}
if (item.right) {
queue.push(item.right);
}
value.push(item.value);
}
return value;
}
带树的深度
function bfs(tree) {
const queue = [];
const value = [];
queue.push(tree);
let index = 0;
while (queue.length !== 0) {
const parents = queue.splice(0);
// 遍历本层的节点
for (let i = 0, l = parents.length; i < l; i++) {
let item = parents[i];
value.push(item.value);
if (item.left) {
queue.push(item.left);
}
if (item.right) {
queue.push(item.right);
}
}
index++;
}
console.log("index", index);
return value;
}
console.log(bfs(tree));
# 实现二叉树深度优先遍历
- 前序遍历 根左右
- 中序遍历 左根右
- 后序遍历 左右根
function dfs(tree) {
const value = [];
function travel(node) {
value.push(node.value); // 前序遍历 根左右
if (node.left) {
travel(node.left);
}
// value.push(node.value); 中序遍历 左根右
if (node.right) {
travel(node.right);
}
// value.push(node.value); 后序遍历 左右根
}
travel(tree);
return value;
}
console.log(dfs(tree));
# 实现cookie的 读 写 删除
cookie 读
function getCookie(cname) {
let name = cname + "=";
let ca = document.cookie.split(";");
for (let i = 0; i < ca.length; i++) {
let c = ca[i].trim();
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
cookie 写
function setCookie(name, value) {
let Days = 30;
let exp = new Date();
exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000);
document.cookie =
name + "=" + escape(value) + ";expires=" + exp.toGMTString();
}
cookie 删除
function deleteCookie(name) {
let exp = new Date();
exp.setTime(exp.getTime() - 1);
let cval = getCookie(name);
if (cval != null)
document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
}
# 实现数字金额千分位
function toThousands(number) {
let string = number.toString();
let result = '';
while(string.length > 3) {
result = ',' + string.slice(-3) + result;
string = string.slice(0, string.length - 3);
}
if(string) {
result = string + result;
}
return result
}
# 实现 getUrlParams(url,key) 获取url的某个参数的问题
# 实现 lastPromise 最后一个promise的结果
思路:和debounce一样做延迟计算
let count = 1;
let promiseFunction = () =>
new Promise((rs) =>
window.setTimeout(() => {
rs(count++);
})
);
function lastPromise(fn) {
let timer = null;
return async (...args) => {
const response = await fn(...args);
clearTimeout(timer);
return new Promise((resolve) => {
timer = setTimeout(() => {
resolve(response);
}, 0);
});
};
}
let lastFn = lastPromise(promiseFunction);
lastFn().then(console.log); // 无输出
lastFn().then(console.log); // 无输出
lastFn().then(console.log); // 3
# 实现并发控制(任务队列) 无缓存
思路:构建task任务对象和任务队列
function SuperTask(maxTaskNumber = 2) {
this.taskQueue = [];
this.runTaskNumber = 0;
this.maxTaskNumber = maxTaskNumber;
}
SuperTask.prototype.add = function (fn) {
return new Promise((resolve, reject) => {
const task = () => {
return fn().then(() => {
resolve();
});
};
this.taskQueue.push(task);
this.run();
});
};
SuperTask.prototype.run = function () {
console.log(this.taskQueue);
while (this.runTaskNumber < this.maxTaskNumber && this.taskQueue.length) {
this.runTaskNumber++;
const task = this.taskQueue.shift();
task().then(() => {
this.runTaskNumber--;
this.run();
});
}
};
function timeout(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
const superTask = new SuperTask();
function addTask(time, name) {
superTask
.add(() => timeout(time))
.then(() => {
console.log(`任务${name}完成`);
});
}
addTask(10000, 1); // 10000ms后输出 任务1完成
addTask(5000, 2); // 5000ms后输出 任务2完成
addTask(3000, 3); // 8000ms后输出 任务3完成
addTask(4000, 4); // 12000ms后输出 任务4完成
addTask(5000, 5); // 15000ms后输出 任务5完成
# 实现 前端并发10个相同的请求,怎么控制为只发一个请求
思路:采取队列的方式,异步任务进入队列
function cacheAsync(fn, symbol) {
const cache = new Map();
const never = Symbol();
return async function (params) {
return new Promise((resolve, reject) => {
symbol = params || symbol;
let cacheCfg = cache.get(symbol);
if (!cacheCfg) {
cacheCfg = {
hit: never,
executor: [{ resolve, reject }],
};
cache.set(symbol, cacheCfg);
} else {
// 命中缓存
if (cacheCfg.hit !== never) {
return resolve(cacheCfg.hit);
}
cacheCfg.executor.push({ resolve, reject });
}
const { executor } = cacheCfg;
// 处理并发,在请求还处于pending过程中就发起了相同的请求
// 拿第一个请求
if (executor.length === 1) {
const next = async () => {
try {
if (!executor.length) return;
const response = await fn(params);
// 如果成功了,那么直接resolve掉剩余同样的请求
while (executor.length) {
// 清空
executor.shift().resolve(response);
}
// 缓存结果
cacheCfg.hit = response;
} catch (error) {
// 如果失败了 那么这个promise的则为reject
const { reject } = executor.shift();
reject(error);
next(); // 失败重试,降级为串行
}
};
next();
}
});
};
}
var fetch2 = cacheAsync(fetchData, "test2");
fetch2(2); // 编号 1
fetch2(2); // 2
fetch2(2); // 3
fetch2(2); // 4
fetch2(2); // 4
fetch2(2); // 5
# 实现一个批量请求函数 multiRequest(urls, maxNum)
要求实现:
- 要求最大并发数 maxNum
- 每当有一个请求返回,就留下一个空位,可以增加新的请求
- 所有请求完成后,结果按照 urls 里面的顺序依次打出
思路:和实现并发控制(任务队列)
的思路类似,采用任务队列
class Task {
constructor(max) {
this.maxNums = max;
this.list = [];
this.runNums = 0;
}
add(fn, index) {
return new Promise((resolve, reject) => {
const task = () => {
return fn()
.then((res) => res.json())
.then((res) => {
const data = res;
resolve({
index: index,
data: data,
});
});
};
this.list.push(task);
this.run();
});
}
run() {
while (this.runNums < this.maxNums && this.list.length !== 0) {
this.runNums++;
const task = this.list.shift();
task().then(() => {
this.runNums--;
this.run();
});
}
}
}
function multiRequest(urls = [], maxNum) {
return new Promise((resolve, reject) => {
let count = 0;
const task = new Task(maxNum);
let result = [];
for (let i = 0; i < urls.length; i++) {
const url = urls[i];
task
.add(() => fetch(url), i)
.then((res) => {
count++;
result[i] = res;
if (count === urls.length) {
resolve(result);
}
});
}
});
}
const urls = [
"http://127.0.0.1:3000/test/1",
"http://127.0.0.1:3000/test/2",
"http://127.0.0.1:3000/test/3",
"http://127.0.0.1:3000/test/4",
"http://127.0.0.1:3000/test/5",
"http://127.0.0.1:3000/test/6",
];
async function run() {
const r = await multiRequest(urls, 3);
console.log("r", r);
}
run();
# 实现 10 个 Ajax 同时发起请求,全部返回展示结果,并且至多允许三次失败,说出设计思路
const send = async (list) => {
let success = 0;
let fail = 0;
const requestList = []
for (let index = 0; index < list.length; index++) {
const url = list[index];
const p = new Promise((resolve, reject) => {
const Ajax = new XMLHttpRequest();
Ajax.open('get', url)
Ajax.onreadystatechange = (() => {
if (Ajax.readyState === 4) {
// console.log(Ajax.response);
// console.log(Ajax.status);
if (Ajax.status === 200) {
success++;
resolve({
success: true,
data: 1
})
} else {
fail++;
resolve({ //<== 此处不要使用reject
success: false,
data: 'error'
})
}
}
})
Ajax.send(null)
})
requestList.push(p)
}
const r = await Promise.all(
requestList
).catch((error) => {
console.log('promise error', error)
return '不会运行到此处'
})
return {
data: r,
success,
fail
};
}
# 实现LRU
let cacheMap = new Map();
const cacheLength = 5;
function lru(key, value) {
if (cacheMap.has(key)) {
cacheMap.delete(key);
}
cacheMap.set(key, value);
if (cacheMap.size > cacheLength) {
const oldKey = Array.from(cacheMap.keys()).shift().toString();
cacheMap.delete(oldKey);
}
console.log('cacheMap', cacheMap);
}
lru('1', '1');
lru('2', '2');
lru('3', '3');
lru('4', '4');
lru('3', '3');
lru('2', '2');
lru('6', '6');
console.log(cacheMap);
# 实现数据的双向绑定
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property);
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
setBind(value, property);
return Reflect.set(target, property, value);
},
};
return new Proxy(obj, handler);
};
let obj = { a: 1 };
let p = onWatch(
obj,
(v, property) => {
console.log(`监听到属性${property}改变为${v}`);
},
(target, property) => {
console.log(`'${property}' = ${target[property]}`);
}
);
p.a = 2; // 监听到属性a改变
p.a; // 'a' = 2
# 实现一个懒加载
两种方式
- 方法1: 计算高度 element.offsetTop < window.innerHeight + document.body.scrollTop
- 方法2: 计算高度 使用getBoundingClientRect().top < window.innerHeight
- 方法3: Intersection Observer API
# 实现a,b的值的交换
两种方式
c = a
a = b
b = c
方法2
a = a + b
b = a - b
a = a - b
# 实现数组去重
const arr1 = [1, 6, 6, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
const arr2 = [];
function uniqueArray1(array) {
const newArray = [];
for (let i = 0; i < array.length; i++) {
if (array.indexOf(array[i]) === i) {
newArray.push(array[i]);
}
}
return newArray;
}
function uniqueArray2(array) {
return [...new Set(array)];
}
function uniqueArray3(array) {
let map = {};
let res = [];
for (var i = 0; i < array.length; i++) {
if (!map[array[i]]) {
map[array[i]] = true;
res.push(array[i]);
}
}
return res;
}
console.log(uniqueArray1(arr1));
console.log(uniqueArray2(arr1));
console.log(uniqueArray3(arr1));
# 实现数组元素求和
const arr = [1, 2, 3, [[4, 5], 6], 7, 8, 9];
function sum(array) {
let num = 0;
for (let index = 0; index < array.length; index++) {
const element = array[index];
if (typeof element === "number") {
num = element + num;
} else {
num += sum(element);
}
}
return num;
}
console.log(sum(arr));
# 实现一个 EventEmitter 类
const EventEmitter = {
events: {},
on(event, listener) {
if (typeof this.events[event] !== "object") {
this.events[event] = [];
}
this.events[event].push(listener);
return () => this.removeListener(event, listener);
},
removeListener(event, listener) {
if (typeof this.events[event] === "object") {
const idx = this.events[event].indexOf(listener);
if (idx > -1) {
this.events[event].splice(idx, 1);
}
}
},
emit(event, ...args) {
if (typeof this.events[event] === "object") {
this.events[event].forEach((listener) => listener.apply(this, args));
}
},
once(event, listener) {
const remove = this.on(event, (...args) => {
remove();
listener.apply(this, args);
});
},
};
export default EventEmitter;
# 实现遍历DOM树
function traversal(node) {
//对node的处理
if (node && node.nodeType === 1) {
console.log(node.tagName);
}
let childNodes = node.childNodes;
let item;
for (let index = 0; index < childNodes.length; index++) {
item = childNodes[index];
if (item.nodeType === 1) {
//递归先序遍历子节点
traversal(item);
}
}
}
Node.ELEMENT_NODE 1 元素节点
# 实现 compose 函数
function compose(...func) {
if(func.length === 0) {
return args => args;
}
if(func.length === 1) {
return func[0];
}
//return funcs.reduce((a, b) => (...args) => a(b(...args)));
return func.reduce((a, b) => {
return (...args) => {
return a(b(...args));
};
});
}
# 实现 async 函数
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里
核心
step(function () {
return gen.next();
});
function async(genF) {
return new Promise(function (resolve, reject) {
const gen = genF();
function step(nextF) {
let next;
try {
next = nextF();
} catch (e) {
return reject(e);
}
if (next.done) {
return resolve(next.value);
}
Promise.resolve(next.value).then(
function (v) {
step(function () {
return gen.next(v);
});
},
function (e) {
step(function () {
return gen.throw(e);
});
}
);
}
step(function () {
return gen.next(undefined);
});
});
}
# 实现 co 函数
//co的实现
function run(fn, ...args) {
return new Promise((resolve, reject) => {
let it;
// fn必须是一个函数
if (typeof fn === "function") {
it = fn(...args);
}
// fn返回的对象必须是一个迭代器
if (!it || typeof it.next !== "function") {
return resolve(it);
}
onResolved();
function onResolved(value) {
let res;
try {
res = it.next(value);
} catch (error) {
return reject(error);
}
next(res);
}
function onRejected(reason) {
let res;
try {
res = it.throw(reason);
} catch (error) {
return reject(error);
}
next(res);
}
function next(res) {
if (res.done) {
return resolve(res.value);
}
// 将结果转换为Promise,然后进行统一处理
Promise.resolve(res.value).then(onResolved, onRejected);
}
});
}
# 实现一个memorize函数
参考lodash的实现:
/**
* @param {Function} func The function to have its output memoized.
* @param {Function} [resolver] The function to resolve the cache key.
* @returns {Function} Returns the new memoized function.
*/
function memoize(func, resolver) {
// 异常过滤
if (typeof func !== "function" || (resolver != null && typeof resolver !== "function")) {
throw new TypeError("Expected a function")
}
// 返回函数 memoized
const memoized = function (...args) {
// 获取到 key:resolver存在则取函数执行结果,否则取参数第一位
const key = resolver ? resolver.apply(this, args) : args[0]
// 缓存判断
// 该写法是直接把缓存对象挂载到函数对象属性cache上
const cache = memoized.cache
if (cache.has(key)) {
return cache.get(key)
}
// 执行函数得到 result
const result = func.apply(this, args)
// 保存到缓存对象 cache 上
memoized.cache = cache.set(key, result) || cache
return result
}
// 指定 cache实例,默认为 Map
memoized.cache = new (memoize.Cache || Map)()
return memoized
}
// 可指定 缓存对象实例
// 例如:替换 memoize.Cache = WeakMap;
memoize.Cache = Map
export default memoize
# 实现一个asyncTry函数
方法一:
function fn() {
return new Promise((resolve, reject) => {
const r = Math.random();
if (r < 0.3) {
resolve("success");
} else {
reject("error");
}
});
}
function asyncRetry(fn, time) {
return new Promise((resolve, reject) => {
let count = 0;
function run() {
fn()
.then((res) => {
resolve(res);
})
.catch((err) => {
count++;
if (count >= time) {
reject(err);
} else {
run();
}
});
}
run();
});
}
asyncRetry(fn, 3).then(console.log, console.log);
方法二:利用async/await能够阻塞for循环
async function asyncRetry(fn, time) {
for (let i = 0; i < time; i++) {
try {
const res = await fn();
return res;
} catch (error) {
if (i === time - 1) {
throw error;
}
}
}
}
asyncRetry(fn, 3).then(console.log, console.log);
# 实现Tom函数
class Tom {
constructor() {
this.list = [];
setTimeout(() => {
this.run();
}, 0);
}
eat(string) {
const fn = () => {
console.log(string);
this.run();
};
this.list.push(fn);
return this;
}
sleep(time) {
const fn = () => {
setTimeout(() => {
console.log("run xx");
this.run();
}, time * 1000);
};
this.list.push(fn);
return this;
}
run() {
const task = this.list.shift();
if (task) {
task();
}
}
}
// 实现Tom函数 满足以下的调用
const tom = new Tom();
tom.eat("123").sleep(3).eat("456").eat("789");