前言

本篇关于尽可能手撕一切可以实现的代码,先从js开始下手

防抖

code

1
2
3
4
5
6
7
8
9
10
11
12
function debounce(fn,timer){
var t = null;
return function(){
let firstClick = !t;
if(firstClick){
fn.apply(this,arguments);
}
t = setTimeout(() => {
t = null;
}, timer);
}
}

节流

code

1
2
3
4
5
6
7
8
9
10
function throttle(fn,delay){
var begin = 0;
return function(){
var cur = +new Date();
if(cur-begin>=delay){
fn.apply(this,arguments);
begin = cur;
}
}
}

浅拷贝

code

1
2
3
4
5
function extend(oldObj,newObj={}){
for (let item in oldObj) {
newObj[item] = oldObj[item];
}
}

深拷贝

api版本

code

1
let newObj = JSON.parse(JSON.stringfy(oldObj));

缺陷:

  • 无法实现对函数,正则等特殊对象的克隆
  • 会抛弃对象的constructor,所有的构造函数会指向Object
  • 对象有循环引用会报错

1.0版本

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function deepClone(obj){
//判断是否是引用类型或者null 否则返回源对象进行浅拷贝
if(typeof obj !== 'object'||obj===null){
return obj;
}
//判断是否是数组,初始化地址。
let result;
if(obj instanceof Array){
result = [];
}else{
result = {};
}
//递归调用
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = deepClone(obj[key]);
}
}
return result;
}

2.0版本 解决循环引用

循环引用

1
2
3
var a = {b:122};
a.target = a;
deepClone(obj);//报错: RangeError: Maximum call stack size exceeded

先方法判断复杂类型
然后用map存储对象 如果已经存在则直接返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const judObj = ((target)=>(typeof target === 'object'||typeof target === 'function')&& target!==null);
const deepClone2 = ((target,map=new Map())=>{
if(map.get(target))return target;
if(judObj(target)){
map.set(target,true);
const res = Array.isArray(target)?[]:{};
for(let key in target){
if(target.hasOwnProperty(key)){
res[key] = deepClone2(target[key],map);
}
}
return res;
}else{
return target
}
})
//测试数据
const a = {val:2};
a.target = a;
let newA = deepClone2(a);
console.log(newA)//{ val: 2, target: { val: 2, target: [Circular] } }

but存在一个潜在的问题,就是map上的key和map构成了强引用关系,是很危险的。
科普强引用和弱引用的关系

被弱引用的对象在什么时候都可以被回收,但强引用的不行,上面的代码在程序结束之前都不会释放a的空间

所以需要用到weekMap构成弱引用,把Map改成weekMap即可

拷贝特殊对象

特殊对象
可遍历的特殊对象:

1
2
3
4
5
["object Map"]
["object Set"]
["object Array"]
["object Object"]
["object Arguments"]

不可遍历的特殊对象:
1
2
3
4
5
6
7
const boolTag = '[object Boolean]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';

用最准确的那个方法判断
然后对于不能遍历的对象直接返回
对于能遍历的对象分类
如果是map的话 foreach 他的item和index赋值
如果是set foreach 他的item
其他的数组和对象 就直接用之前的方法遍历递归
注意要保留对象的原型
code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const judObj = ((target)=>(typeof target === 'object'||typeof target === 'function')&& target!==null);

const getObj = ((target)=>Object.prototype.toString.call(target));

const canTranverse = {
'[object Map]': true,
'[object Set]': true,
'[object Array]': true,
'[object Object]': true,
'[object Arguments]': true,
}

const deepClone3 = ((target,map=new Map)=>{
if(!judObj)return;
let type = getObj(target);
let cloneTarget;
if(!canTranverse[type]){
return;
}else{
let ctor = target.prototype;
cloneTarget = new ctor();
}

if(map.get(cloneTarget)){
return;
}else{
map.set(cloneTarget,true);
}

if(type === '[object Map]'){
target.forEach((item,index) => {
cloneTarget.set(deepClone3(item),deepClone3(index));
});
}
else if(type === '[object Set]'){
target.forEach(item=>{
cloneTarget.add(deepClone3(item));
})
}
else{
for (let key in target) {
if (target.hasOwnProperty(key)) {
cloneTarget[key] = deepClone3(target[key],map);
}
}
}
return cloneTarget;
})

3.0 final

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
const getType = obj => Object.prototype.toString.call(obj);

const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;

const canTraverse = {
'[object Map]': true,
'[object Set]': true,
'[object Array]': true,
'[object Object]': true,
'[object Arguments]': true,
};
const mapTag = '[object Map]';
const setTag = '[object Set]';
const boolTag = '[object Boolean]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const dateTag = '[object Date]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]';

const handleRegExp = (target) => {
const { source, flags } = target;
return new target.constructor(source, flags);
}

const handleFunc = (func) => {
// 箭头函数直接返回自身
if(!func.prototype) return func;
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
// 分别匹配 函数参数 和 函数体
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if(!body) return null;
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
}

const handleNotTraverse = (target, tag) => {
const Ctor = target.constructor;
switch(tag) {
case boolTag:
return new Object(Boolean.prototype.valueOf.call(target));
case numberTag:
return new Object(Number.prototype.valueOf.call(target));
case stringTag:
return new Object(String.prototype.valueOf.call(target));
case symbolTag:
return new Object(Symbol.prototype.valueOf.call(target));
case errorTag:
case dateTag:
return new Ctor(target);
case regexpTag:
return handleRegExp(target);
case funcTag:
return handleFunc(target);
default:
return new Ctor(target);
}
}

const deepClone = (target, map = new WeakMap()) => {
if(!isObject(target))
return target;
let type = getType(target);
let cloneTarget;
if(!canTraverse[type]) {
// 处理不能遍历的对象
return handleNotTraverse(target, type);
}else {
// 这波操作相当关键,可以保证对象的原型不丢失!
let ctor = target.constructor;
cloneTarget = new ctor();
}

if(map.get(target))
return target;
map.set(target, true);

if(type === mapTag) {
//处理Map
target.forEach((item, key) => {
cloneTarget.set(deepClone(key, map), deepClone(item, map));
})
}

if(type === setTag) {
//处理Set
target.forEach(item => {
cloneTarget.add(deepClone(item, map));
})
}

// 处理数组和对象
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
cloneTarget[prop] = deepClone(target[prop], map);
}
}
return cloneTarget;
}

实现发布订阅EventEmitter

发布订阅者模式,一种对象间一对多的依赖关系,但一个对象的状态发生改变时,所依赖它的对象都将得到状态改变的通知。

主要的作用(优点):

广泛应用于异步编程中(替代了传递回调函数)
对象之间松散耦合的编写代码
缺点:

创建订阅者本身要消耗一定的时间和内存
多个发布者和订阅者嵌套一起的时候,程序难以跟踪维护
实现的思路:

创建一个对象(缓存列表)
on方法用来把回调函数fn都加到缓存列表中
emit 根据key值去执行对应缓存列表中的函数
off方法可以根据key值取消订阅
发布订阅者模式和观察者模式的区别?

发布/订阅模式是观察者模式的一种变形,两者区别在于,发布/订阅模式在观察者模式的基础上,在目标和观察者之间增加一个调度中心。
观察者模式是由具体目标调度,比如当事件触发,Subject 就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的。
发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 发布订阅者模式
// 一种对象间一对多的依赖关系,其中一个对象的状态发生改变的时候,所依赖他的对象都将得到状态改变的通知

class EventEmitter {
constructor() {
this.event = {};
}
subscribe(type, callback) {
if (!this.event) this.event = Object.create(null);
if (!this.event[type]) {
this.event[type] = [callback];
} else {
this.event[type].push(callback);
}
}
unsubscribe(type, callback) {
if (!this.event) return;
this.event[type] = this.event[type].filter((item) => item !== callback);
}
once(type, callback) {
function fn(...args) { //因为是绑定完之后移除 所以要触发 触发的方式通过cb传参注意!
callback(args);
this.unsubscribe(type, fn);
}
this.subscribe(type, fn);
}
publish(type, ...args) {
if (!this.event) return;
this.event[type] && this.event[type].forEach((fn) => fn.apply(this, args));
}
}

let myevent = new EventEmitter(); // 创建一个调度中心

let task1 = function (...args) {
console.log("发布任务 任务信息", args);
};
let task2 = function (...args) {
console.log("发布任务2 任务信息", args);
};

// case1 在调度中心中的任务只要订阅了就一直可以发布 除非解除订阅
myevent.subscribe('task1',task1)//订阅任务
myevent.publish('task1','ko no dio da');//发布任务
myevent.publish('task1','ko no dio da2');//发布任务
myevent.unsubscribe("task1", task1); // 解除订阅
myevent.publish('task1','ko no dio da3');//发布任务
// 结果
// 发布任务 任务信息 [ 'ko no dio da' ]
// 发布任务 任务信息 [ 'ko no dio da2' ]

// case2 在调度中心中的任务只要如果执行once那么只能发布一次
myevent.once('task2',task2)//订阅任务
myevent.publish('task2','ko no jojo da');//发布任务
myevent.publish('task2','ko no jojo da2');//发布任务
myevent.unsubscribe("task2", task2); // 解除订阅
myevent.publish('task2','ko no jojo da3');//发布任务
// 结果
// 发布任务2 任务信息 [ [ 'ko no jojo da' ] ]


实现观察者模式

观察者模式 是基于发布订阅的
发布订阅有一个调度中心,但是观察者不需要调度中心,被观察者中直接存储所有的观察者,当情况发生变化的时候去通知所有的观察者

那么被观察者中就有两个函数,一个是收集观察者的函数,一个更新自身状态的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Subject{
constructor(name){
this.state = 'happy';//状态
this.observers = [];//存储观察者
this.name = name;//被观察者名称
}
attach(o){
this.observers.push(o);
}
setState(newState){
this.state = newState;
this.observers.forEach(o=>o.update(this));
}
}

class Observer{
constructor(name){
this.name = name;//观察者名称
}
update(subject){
console.log('当前观察者'+this.name+'被通知了,当前被观察者'+subject.name+'状态是'+subject.state)
}
}

let student = new Subject('学生');
let parent = new Observer("父母");
let teacher = new Observer("老师");

// 被观察者存储观察者的前提,需要先接纳观察者
student.attach(parent);
student.attach(teacher);
student.setState("被欺负了");

instanceof

原理 找到实例对象的原型然后不断往上查找,如果和类实例的原型相同则为真,如果是null则为假

1
2
3
4
5
6
7
8
function myinstanceof(example,classFunc){
let proto = Object.getPrototypeOf(example);
while(true){
if(proto===null)return false;
if(proto===classFunc.prototype)return true;
proto = Object.getPrototypeOf(proto);
}
}

new

模拟new就要知道new操作符做了什么事情

  • 将对象的隐式原型指向构造函数的原型prototype
  • 执行构造函数,使用call/apply改变this指向
  • 返回值为object类型则作为new方法的返回值返回,否则返回上述全新对象。
1
2
3
4
5
function mynew(fn,...args){
let instance = Object.create(fn.prototype);
let res = fn.apply(instance,args);
return typeof res === 'object'?res:instance;
}

call

call不传入参数则默认绑定的window
新建函数将函数设置当前this
然后删除该函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function.prototype.myCall = function(ctx = window,...args){
const fn = Symbol('fn');
ctx[fn] = this;
const res = ctx[fn](...args);
delete ctx[fn];
return res;
}
//用法:f.call(obj,arg1)
function f(a, b) {
console.log(a + b)
console.log(this.name)
}
let obj = {
name: 1
}
f.myCall(obj, 1, 2) //否则this指向window

apply

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Function.prototype.myApply = function(ctx=window,args){
try {
if(args&&!Array.isArray(args)){
throw Error('必须是数组');
}
const fn = Symbol('fn');
ctx[fn] = this;
const res = ctx[fn](...args);
delete ctx[fn];
return res;
} catch (error) {
console.log(error)
}
}
//用法:f.apply(obj,[arg1])
function f(a, b) {
console.log(a + b)
console.log(this.name)
}
let obj = {
name: 1
}
f.myApply(obj, [1, 2]) //否则this指向window
f.myApply(obj, 1,2) //否则this指向window

bind

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Function.prototype.mybind = function (context) {
if (typeof this !== 'function') {
throw new TypeError('err');
}
var self = this;
var args = [...arguments].slice(1);
return function F() {
//判断是否是new 是的话变成指向function 否则指向context
var _this = this instanceof self ? this : context;
//改变f的原型链
F.prototype = self.prototype;
return self.apply(_this, args.concat(...arguments));
}
}

Object.create

1
2
3
4
5
function create(proto){
function F(){};
F.prototype = proto;
return new F();
}

类的继承

Promise.resolve

  1. 值是promise对象则直接返回
  2. 返回一个new promise且如果他是then调用,则返回then方法
  3. 其他情况,返回成功状态的promise
    code
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 1. promise return
    // => new promise
    // 2. param.then === 'fun'
    // => param.then(res,rej)
    // 3. return resolve(param)

    Promise.resolve = (param => {
    if (param instanceof Promise) return param;
    return new Promise((resolve, reject) => {
    if (param && param.then && typeof param.then === 'function') {
    return param.then(resolve, reject);
    } else {
    return resolve(param)
    }
    })
    })

Promise.reject

reject传入的参数会作为一个reason原封不动的往下传

1
2
3
4
5
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}

Promise.prototype.finally

不管状态如何都会执行并且往下传值.

1
2
3
4
5
6
7
8
9
10
11
Promise.prototype.finally = function(cb){
this.then(value=>{
return Promise.resolve((cb()).then(()=>{
return value;
}))
},error=>{
return Promise.resolve((cb()).then(()=>{
throw error;
}))
})
}

Promise.all

code如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Promise.myall = function(iterable){
//传入的是可迭代对象
let index = 0; //这里不一定是数组,不一定有length,所以要变量计算.
let elementCount = 0; //解决的promise数量
let anErrorOccurred = false;//判断是否错误
for (const promise of iterable) {
const curIndex = index; //封闭index的作用域
promise.then((value)=>{
if(anErrorOccurred)return;
result[curIndex] = value;
elementCount++;
if(elementCount === result.length){
resolve(result)//如果全部任务都成功,返回数组
}
},(err)=>{
if(anErrorOccurred)return;
anErrorOccurred=true;
reject(err);
})
index++;
}
if(index === 0){
resolve([]);//长度是0 返回空数组
return;
}
const result = new Array(index);//要在最后写result 因为index是在循环后才计算出来.
}

Promise.race

race方法只要有一个率先改变了状态,后面就直接resolve

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Promise.myrace = function (iterable) {
let settlementOccurred = false;
for (const promise of iterable) {
promise.then((value) => {
if (settlementOccurred) return;
settlementOccurred = true;
resolve(value);//直接resolve
}, (err) => {
if (settlementOccurred) return;
settlementOccurred = true;
reject(err);//直接reject
})
}
}

Promise.allSettled

执行完后不会失败,按顺序返回每个promise状态

1
2
3
4
5
6
7
8
9
10
11
const resolved = Promise.resolve(2);
const rejected = Promise.reject(-1);
const allSettledPromise = Promise.allSettled([resolved, rejected]);
allSettledPromise.then(function (results) {
console.log(results);
});
// 返回结果:
// [
// { status: 'fulfilled', value: 2 },
// { status: 'rejected', reason: -1 }
// ]

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Promise.allSettled = function (promiseArgs) {
if (!promiseArgs.length) return Promise.resolve([]);
// 包装不是promise的项
const promises = promiseArgs.map(p => p instanceof Promise ? p : Promise.resolve(p));
return new Promise((resolve, reject) => {
let res = [];
let unSettledCount = promises.length;
promises.forEach((p, index) => {
p.then((reason) => {
res[index] = {
status: "resolve",
reason
}
}, (reason) => {
res[index] = {
status: "reject",
reason
}
}).finally(() => {
--unSettledCount;
if (!unSettledCount) return resolve(res);
})
});
})
}

Promise.retry超时请求

简易版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Promise.retrySimple = function (fn, maxRetry, timeout) {
if (typeof fn !== 'function') {
throw new TypeError('Expected a function');
}
maxRetry = maxRetry || 3;
timeout = timeout || 1000;

let retryCount = 0;
return new Promise((resolve, reject) => {
const run = () => {
fn().then((value) => {
resolve(value);
}, (err) => {
if (retryCount >= maxRetry) {
reject(err);
return;
}
setTimeout(run,timeout);
retryCount++;
})
}
run();
})
}

有缓存版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 完整版 - 有缓存
Promise.retry = function (fn, options) {
// 参数检查
if (typeof fn !== 'function') {
throw new TypeError('Expected a function');
}
options = options || {};
// 默认参数
options = Object.assign({
maxRetry: 3, // 默认重试次数
retryDelay: 1000, // 默认重试时间间隔
cache: false, // 是否缓存结果
cacheKey: "", // 缓存 key
cacheExpire: 0, // 缓存过期时间,单位:毫秒
cacheMax: 0 // 缓存最大值,超过后清空缓存
}, options);
let retryCount = 0; // 已重试次数
return new Promise((resolve, reject) => {
// 内部函数,进行一次尝试
const run = () => {
fn().then(
(value) => {
// 成功收到响应,如果需要缓存,则缓存结果,同时设置缓存过期时间
if (options.cache) {
localStorage.setItem(options.cacheKey, JSON.stringify({
value,
expire: Date.now() + options.cacheExpire
}));
}
// 封装的 promise 解决
resolve(value);
},
(err) => {
// 超过了最大重试次数,拒绝
if (retryCount >= options.maxRetry) {
reject(err);
return;
}
// 没有超过重试次数,如果有缓存,则读取缓存
if (options.cache) {
const cache = localStorage.getItem(options.cacheKey);
if (cache) {
const cacheObj = JSON.parse(cache);
if (cacheObj.expire > Date.now()) {
resolve(cacheObj.value);
return;
}
}
}
// 重试
setTimeout(run, options.retryDelay);
retryCount++;
});
};
run();
});
};

解析URL Params对象

实现JS函数柯里化

柯里化的定义:接收一部分参数,返回一个函数接收剩余参数,接收足够参数后,返回原函数。

思路如下:

  1. 利用length属性获取函数的形参个数
  2. 手动指定需要的形参个数

ES5版本(涉及到arguments的es5转换法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function mycurry(fn,args){
const len = fn.length;
var args = args || [];
return function (){
//arguments的逐次分割
var newArgs = args.concat(Array.prototype.slice.call(arguments));
//不满足携带newArgs递归
if(newArgs.length<len){
return mycurry.call(this,fn,newArgs);
}else{
//满足直接返回
return fn.apply(this,newArgs);
}
}
}
function multiFn(a,b,c){
return a*b*c
}
var fn = mycurry(multiFn);

console.log(fn(2)(3)(4))
console.log(fn(2,3,4))
console.log(fn(2)(3,4))
console.log(fn(2,3)(4))

ES6版本
es6可以直接用拓展运算符所以会写起来简单一点

1

数组扁平化

要求:

1
2
3
let arr = [1,[2,[3,4],5],6]
=>
[1,2,3,4,5,6]

方法1:api版本

1
2
let arr = [1,[2,[3,4],5],6];
console.log(arr.flat(Infinity))

方法2:reduce

1
2
3
4
5
6
7
let arr = [1,[2,[3,4],5],6];
function flatten(arr){
return arr.reduce((pre,cur)=>{
return pre.concat(cur instanceof Array?flatten(cur):cur);
},[])
}
console.log(flatten(arr))

方法3:转字符串

1
2
3
4
5
let arr = [1,[2,[3,4],5],6];
function flatten(arr){
return arr = arr.join(",").split(",").map(item=>~~item);
}
console.log(flatten(arr))

解析URL

要求:可以传入数组或者对象,编写一个myURLSearchParams类,包含get,set,has,append

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"foo=1&bar=2"
{
foo: "1",
bar: "2"
}

console.log(searchParams.get("foo")) //1
console.log(searchParams2.get("foo")) //1
searchParams.set("foo","33");
searchParams2.set("foo","33");
console.log(searchParams) //{ url: 'foo=33&bar=2' }
console.log(searchParams2) //{ url: { foo: '33', bar: '2' } }
console.log(searchParams.has("foo")) //true
console.log(searchParams2.has("foo")) //true
searchParams.append("foo","3casc3"); //{ url: 'foo=33&bar=2&foo=3casc3' }
searchParams2.append("foo","3casc3"); // url: { foo: '3casc3', bar: '2' } }

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class myURLSearchParams {
constructor(url) {
this.url = url;
}
get(target) {
if (this.url instanceof Object) {
return this.url[target]
} else {
let arr = this.url.split('&');
for (let item of arr) {
let [key, val] = item.split('=');
if (key === target) {
return val;
}
}
}
}
append(key,value){
if(this.url instanceof Object){
this.url[key] = value;
}else{
this.url+=("&"+key+"="+value);
}
}
set(key,value){
if(this.url instanceof Object){
this.url[key] = value;
}else{
let arr = this.url.split('&');
for (let item of arr) {
let [keys, val] = item.split('=');
if (keys === key) {
let rep = `${key}=${val}`
let rep2 = `${key}=${value}`
this.url = this.url.replace(rep,rep2)
break;
}
}
}
}
has(target){
if(this.url instanceof Object){
return this.url[target]?true:false;
}else{
return this.url.match(target)?true:false;
}
}
}
let searchParams = new myURLSearchParams("foo=1&bar=2");
let searchParams2 = new myURLSearchParams({
foo: "1",
bar: "2"
});

删除指定的URL中的某个参数

需求:删除URL中的某个参数并且确保没有重复的出现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let query = 'https://www.baidu.com/s?ie=utf-8&f=8&key2=99&key2=99';

function myQueryDelete(query,target){
let queryArr = query.split("?")[1].split("&");
console.log(queryArr)
let index = 0;
let len = queryArr.length-1;
for (let item of queryArr) {
let [key,value] = item.split("=");
if(key === target){
if(index===len){
query = query.replace(`${key}=${value}`,'')
}else{
query = query.replace(`${key}=${value}&`,'')
}
}
index++;
}
while(query[query.length-1]==='&'){
query = query.slice(0,-1);
}
console.log(query)
}

myQueryDelete(query,"key2")
//https://www.baidu.com/s?ie=utf-8&f=8

js生成二叉树

需求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let testData = [{
id: 5,
parent: 4
},
{
id: 2,
parent: 0
},
{
id: 3,
parent: 1
},
{
id: 1,
parent: 0
},
{
id: 4,
parent: 1
},
{
id: 0,
parent: -1
},
]
转二叉结构

code如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const toTree = (arr => {
let map = {};
let res = [];
for (const item of arr) {
map[item.id] = {
...item,
children: []
};
}
for (const item of arr) {
let id = item.id;
let pid = item.parent;
let treeItem = map[id];
if (pid === -1) {
res.push(treeItem);
} else {
map[pid].children.push(treeItem)
}
}
console.log(map)
})
toTree(testData)

转化为驼峰命名

利用正则

1
2
3
4
5
6
7
8
9
10
11
var s1 = "get-element-by-id"
const fn = (word)=>{
// 匹配-\w,并第一个字母替换成大写字母
// 利用到了replace的第二个参数,传输函数处理
return word.replace(/-\w/g,(x)=>{
return x.slice(1).toUpperCase();
})
}
console.log(
fn(s1)
)

颜色转换

实现AJAX

Promise实现ajax

实现LRU淘汰算法