手写call

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
Function.prototype.myCall = function(context,...args){
//1.判断是否是函数调用的方法
if(typeof this !== 'function'){
throw new typeError('not a function')
}
//2.判断是否传入了上下文对象
context = context||window
//3.改变this的指向
context.fn = this
let res = context.fn(...args)
delete context.fn
return res
}

let obj1 = {
getName(...args){
console.log(this.name+args[0]+args[1])
}
}

let shili = {
name:'shaking'
}

obj1.getName.myCall(shili,1,2,3,4)

手写apply

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
Function.prototype.myApply = function(context,args){

if (typeof this !== 'function') {
throw new TypeError('not a function')
}

//context有和无的情况
context = context||window
//arguments是伪数组 context只能接收到传入的第一个参数

//改变this指向
context.fn = this
//执行函数
res = context.fn(args)
delete context.fn
return res
}
var sobj = {
getName:function(a){
return this.name+a
}
}
var person = {
name:"shaking"
}

console.log(sobj.getName.myApply(person,[1,2,3]))

手写bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Function.prototype.myBind = function (context, ...args) {
//明确:this要绑定到context上
//1. this放到一个变量中
let _me = this;
//2. 新建空的构造函数,用来实例化
let fn = function () { }
//3. this改变后的函数
let toBeBind = function () {
//检测是否是new绑定
let _this = this instanceof toBeBind ? this : context;
//执行
return _me.apply(_this, [...args, ...arguments]);
}
fn.prototype = _me.prototype;
toBeBind.prototype = new fn();
return toBeBind;
}

let fn = new fn1.myBind(obj)

手写new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype.myNew= function(constructor,...args){
//创建一个对象 ES5提供了Object.create方法,该方法可以创建一个对象,并让新对象的__proto__属性指向已经存在的对象。
// let o = Object.create(constructor.prototype);
//具体步骤:
//1.创建一个空对象

var o = {};
//2.把空对象上的__proto__属性指向constructor的原型对象
o.__proto__ = constructor.prototype
//3.把constructor的this绑定到了新建的对象上
let result = constructor.apply(o,args);
//4.判断调用构造函数的结果,是对象就返回,不是对象那就返回新对象
return result&&typeof(result == 'object')?result:o;

}

实现Object.creact

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//实现object.create..方法的第一个参数是作为新对象__proto__属性的值,第二个参数是一个对象,里面的每一个属性都会作为新对象的属性,可以用
Object.prototype.myCreate = function (proto, propertyObj = undefined) {
if (proto === null) {
throw 'TypeError';
} else {
function fn() { };
fn.prototype = proto;//有创建了一个新的构造函数,继承了proto的原型。为什么要有这看似多余的一步?保证只有proto上的属性和方法只是被“继承”而不是直接使用。
const obj = new fn();//obj是实例对象有__proto__属性指向原型对象也就是构造函数的原型,这一步使得obj对象的__proto__属性指向fn的prototype
if (propertyObj !== undefined) {
Object.defineProperties(obj, propertyObj);
}
if (proto === null) {
obj.__proto__ = null;
}
return obj;
}
}

手写instanceof

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function myInstanceof(left, right) {
let L = left
while (true) {
if (L.__proto__ == null) {
return false
}
if (L.__proto__ == right.prototype) {
return true
}
L = L.__proto__
}
}
let a = [1, 2, 3, 4]
let b = {
a: 1
}
let res = myInstanceof(a, Object)
console.log(res)

手写深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function deepClone(value,map= new WeakMap()) {
if (value == null) return value;
if (typeof value !== 'object') return value;
if (value instanceof RegExp) return new RegExp(value);
if (value instanceof Date) return new Date(value);
// 我要判断 value 是对象还是数组 如果是对象 就产生对象 是数组就产生数组
let cloneObj = Array.isArray(value)?[]:{};
if(map.get(value)) {
return map.get(value);
}
map.set(value,cloneObj);
for(let key in value){
if(value.hasOwnProperty(key)) {
cloneObj[key] = deepClone(value[key],map);
}
}
return cloneObj;
}

防抖函数

1
2
3
4
5
6
7
8
9
function debounce(fn, wait) {
let timer = null;
return function () {
clearTimeout(timer);
timer = setTimeout(()=> {
fn.apply(this, arguments)
}, wait);
}
}

节流函数

1
2
3
4
5
6
7
8
9
10
11
12
13
function throttle(fn, wait) {
let timer = null
return function () {
if (timer) {
return
} else {
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, wait)
}
}
}

实现函数柯里化/实现add(1)(2)(3)

函数柯里化指的是将能够接收多个参数的函数转化为接收单一参数的函数,并且返回接收余下参数和结果的新函数的技术。

好处是让函数参数的处理更加自由,可以用作工具函数。

举例来说,一个接收3个参数的普通函数,在进行柯里化后, 柯里化版本的函数接收一个参数并返回接收下一个参数的函数, 该函数返回一个接收第三个参数的函数。 最后一个函数在接收第三个参数后, 将之前接收到的三个参数应用于原普通函数中,并返回最终结果。

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
//普通函数
function add (...args) {
//求和
return args.reduce((a, b) => a + b)
}

function currying (fn) {
let args = []
return function temp (...newArgs) {
if (newArgs.length) {
args = [
...args,
...newArgs
]
return temp
} else {
let val = fn.apply(this, args)
args = [] //保证再次调用时清空
return val
}
}
}

let addCurry = currying(add)
console.log(addCurry(1)(2)(3)(4, 5)()) //15
console.log(addCurry(1)(2)(3, 4, 5)()) //15
console.log(addCurry(1)(2, 3, 4, 5)()) //15

手写promise

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
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected"
class myPromise {
constructor(executor) {
try {
executor(this.resolve, this, this.reject);
} catch {
this.reject(err);
}
}
state = PENDING;
fulfilledCallbacks = [];
rejectedCallbacks = [];
value = null;
reason = null;
resolve = (value) => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
while (this.fulfilledCallbacks.length) this.fulfilledCallbacks.shift()(value);
}
}
reject = (reason) => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
while (this.rejectedCallbacks.length) this.rejectedCallbacks.shift()(value);
}
}
then(onFulfilled, onRejected) {
//不传参数时默认
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason };
const promise2 = new myPromise((resolve, reject) => {
const resolveMicrotask = () => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value);
this.resolvePromise(x, promise2, resolve, reject);
} catch (err) {
reject(err);
}
})
}
const rejectMicrotask = () => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason);
this.resolvePromise(x, promise2, resolve, reject);
} catch (err) {
reject(err);
}
})
}
if (this.state === FULFILLED) resolveMicrotask();
else if (this.state === REJECTED) rejectMicrotask();
else if (this.state === PENDING) {
this.fulfilledCallbacks.push(onFulfilled);
this.rejectedCallbacks.push(onRejected);
}
})
return promise2;
}
resolvePromise(x, self, resolve, reject) {
if (x === self) {
throw new Error("")
}
if (x instanceof myPromise) {
x.then(resolve, reject);
} else {
resolve(x);
}
}

}

promise.all

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//接收一个promise数组,所有promise都成功则返回成功结果数组,有一个失败则返回这个失败结果。额外注意数组中如果有不为promise类型的则算作成功。
function all(arr) {
let resArr = [];
let count = 0;
return new Promise((resolve, reject) => {
arr.forEach((el, index) => {
if (el instanceof Promise) {
Promise.resolve(el).then(res => {
resArr[index] = res;
count++;
}, err => reject(err))
} else {
resArr[index] = el;
count++;
}
if (count === arr.length) {
resolve(resArr);
}
});
})

}

promise.allSettled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//同样也是接收promise数组,跟all不同的是不管成功失败都会把处理结果放进结果数组中。
function allSettled(arr) {
let resArr = [];
let count = 0;
arr.forEach((el, index) => {
if (el instanceof Promise) {
Promise.resolve(el).then(res => {
resArr[index] = res;
count++;
}, err => {
resArr[index] = err;
count++;
})
} else {
resArr[index] = el;
count++;
}
if (count === arr.length) {
resolve(resArr);
}
})
}

promise.any

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//只要有一个promise成功则返回结果,全部失败则报错function 
any(arr) {
let count = 0;
return new Promise((resolve,reject)=>{
arr.forEach((el) => {
if(el instanceof Promise) {
Promise.resolve(el).then(res=>{
resolve(res);
},err=>{
count++;
if(count === arr.length) {
reject(new Error("All promises were rejected"));
}
})
}else{
resolve(el);
}
})
})

}

promise.race

1
2
3
4
5
6
7
8
9
10
11
12
13
//promise.race 传入的promise数组中先出结果的值作为返回的promise的值。
function race(arr) {
return new Promise((resolve, reject) => {
arr.forEach(el => {
if (el instanceof Promise) {
Promise.resolve(el).then(res => resolve(res), err => reject(err))
} else {
resolve(el);
}
})

})
}

promise.finally

1
2
3
4
5
6
7
8
9
Promise.prototype.finally = function(callback) {
return this.then(res=>{
callback();
return res;
},err=>{
callback();
throw err;
})
}

实现数组扁平化

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
//1.递归 使用额外空间
const res = [];
const fn = arr => {
for(let i = 0;i<arr.length;i++) {
if(Array.isArray(arr[i])) {
fn(arr[i]);
}else {
res.push(arr[i]);
}
}
}
let arr = [1, [2, [3, [4, 5]]], 6,[1,2,[3,4]]];
fn(arr);
//2.使用flat
const res1 = arr.flat(Infinity)
//3.toString/join自动扁平化,split分割
const myFlat = arr => {
return arr.toString().split(",").map(i=>Number(i))
}
let res2 = myFlat(arr);
console.log(res2);
//4.reduce
const myFlat2 = arr => {
return arr.reduce((pre,cur) => {
pre.concat(Array.isArray(cur)?flat(cur):cur)
},[])
}
//5.扩展运算符 [].concat(...arr)//只能扁平化一层
const myFlat3 = arr => {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}

数组去重

1
2
3
4
5
6
7
8
9
10
11
12
//1.es6 set
const unique = (arr) => {
return [...new Set(arr)];
}
//2.indexof/includes
const unique2 = (arr) => {
return arr.filter((val,index) => {
return arr.indexOf(val) === index;
})
}

[1,2,[1,3,[4,5],[1,2]],6,7,8,[6,8]]

数组转树状结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function convert(list) { 
const res = [];
const idMapping = data.reduce((acc,el,i)=>{
acc[el.id] = i;
return acc
},{})
let root;

data.forEach(el => {
if(el.parentId===null) {
root = el;
}
const parentEl = data[idMapping[el.parentId]];
parentEl.children = [...(parentEl.children||[]),el]
});
}

树状结构转数组

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 res = [];
function treeToArray(obj) {
res.push(obj);
if(obj.children.length) {
for(const item of obj.children) {
treeToArray(item);
}
}
return res;

}
//法2
function treeToArray2(obj) {
const stack = [];
const res = [];
stack.push(obj);
while(stack.length) {
const item = stack[0];
res.push(item);
stack.pop();
if(item.children&&item.children.length) {
stack.push(...item.children)
}
}
return res;
}

排序

冒泡排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const bubble = (nums) =>{
for(let i=0;i<nums.length;i++){//
for(let j=0;j<nums.length-1-i;j++){
if(arr[j]>arr[j+1]){
let temp = arr[j+1];
arr[j+1] = arr[j];
arr[j] = temp;
}
}
}
return nums;
}
//思想:两层循环,每次两两比较元素大小,把待排序部分最大的元素排到末尾。优点:如果检测到数组有序,不会交换

快速排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const quickSort = array => {
if(array.length<=1){
return array
}
let left = []
let right = []
let pIndex = Math.floor(array.length/2)
let p = array.splice(pIndex,1)[0]
for(let i=0;i<array.length;i++){
if(array[i]<p){
left.push(array[i])
}
else{
right.push(array[i])
}
}
return quickSort(left).concat([p], quickSort(right))
}
//快速排序mlogn 不稳定 最坏情况的时间复杂度为O(n²);

归并排序

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
//归并排序 nlogn 稳定 外排序
const mergeSort = (arr) => {
var len = arr.length;
if(len<2) {
return arr;
}
let middle = Math.floor(len/2);
let left = arr.slice(0,middle);
let right = arr.slice(middle);
return merge(mergeSort(left),mergeSort(right));
}

const merge = (left,right) => {
let res = [];
console.time("11");
while(left.length&&right.length) {
if(left[0]<=right[0]) {
res.push(left.shift());
} else {
res.push(right.shift());
}
}
while(left.length) {
res.push(left.shift());
}
while(right.length) {
res.push(right.shift());
}
console.timeEnd("22");
return res;
}


const arr = [5,4,6,3,6,8,9,2];
let res = mergeSort(arr);
console.log(res);