# 12. 手撕代码

# 实现一个call函数

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。 该方法的语法和作用与 apply() 方法类似,只有一个区别,就是 call() 方法接受的是一个参数列表,而 apply() 方法接受的是一个包含多个参数的数组。

function.call(thisArg, arg1, arg2, ...)

Function.prototype.call = function (context,...args) {
 let context = context ? context : window;
 let args = args ? args : [];
 let key = Symbol();
 context[key] = this;
 let res = context[key](...args);
 delete context[key];
 return res;
}
1
2
3
4
5
6
7
8
9

# 实现一个apply函数

apply() 方法调用一个具有给定this值的函数,以及作为一个数组(或类似数组对象)提供的参数。

func.apply(thisArg, [argsArray])

Function.prototype.apply = function (context,args) {
 let context = context ? context : window;
 let args = args ? args : [];
 let key = Symbol();
 context[key] = this;
 let res = context[key](...args);
 delete context[key];
 return res;
}
1
2
3
4
5
6
7
8
9

# 实现一个bind函数

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。

function.bind(thisArg[, arg1[, arg2[, ...]]])

Function.prototype.bind = function (context,...args) {
  if(typeof this !== 'function'){
  	return new TypeError('must be function');
  }
  let _this=this;
  return function F(...newArgs){
  	if(this instanceof F){
		return new _this(...args,...newArgs);
	}
	return _this.apply(context,...args,...newArgs);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

# 实现new功能

new 运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象的实例。new 关键字会进行如下的操作:

  1. 创建一个空的简单JavaScript对象(即{});
  2. 链接该对象(即设置该对象的构造函数)到另一个对象 ;
  3. 将步骤1新创建的对象作为this的上下文 ;
  4. 如果该函数没有返回对象,则返回this。
function create(fn,...args){//fn是要new的函数,args是函数的参数
  // 创建一个空对象obj,然后将fn的原型链接到obj的原型上
  let obj = Object.create(fn.prototype);
  // 绑定 this 实现继承,obj 可以访问到构造函数中的属性
  let res = fn.apply(obj,args);
  // 优先返回构造函数返回的对象,object/array/function优先返回,如果是其他类型则返回obj
  return res instanceof Object ? res : obj;
}
1
2
3
4
5
6
7
8

# 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__
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 浅拷贝

function extend (a, b) {
  for (const key in b) {
    a[key] = b[key]
  }
  return a
}
1
2
3
4
5
6

# 深拷贝

function clone (value) {
  if (Array.isArray(value)) {
    return value.map(clone)
  } else if (value && typeof value === 'object') {
    const res = {}
    for (const key in value) {
      res[key] = clone(value[key])
    }
    return res
  } else {
    return value
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 节流

const throttle = (fn, delay) => {
  let flag = true;
  return (...args) => {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fn.apply(this, args);
      flag = true;
    }, delay);
  }
}

1
2
3
4
5
6
7
8
9
10
11
12

# 防抖

const debounce = (fn, delay) => {
  let timer = null;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  }
}

1
2
3
4
5
6
7
8
9
10

# 遍历二叉树

function lvl(root) {
  let res = [];
  const dep = (node, level) => {
    if (!node) return;
    res[level] ? res[level].push(node.val) : res[level] = [node.val];
    level++;
    dep(node.left, level);
    dep(node.right, level);
  }
  dep(root, 0);
  return res;
}
1
2
3
4
5
6
7
8
9
10
11
12

# Promise.all

Promise.all = function(arr) {
  return new Promise((resolve, reject) => {
    let ans = [];
    let count = 0;
    for(let i=0; i<arr.length; i++) {
      arr[i].then(val => {
        ans[i] = val;
        count++;
        if (count === arr.length) {
          resolve(ans);
        }
      }, err => {
        reject(err)
      })
    }
  });
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Promise.allSettled

Promise.allSettled = function(arr) {
  return new Promise((resolve, reject) => {
    let ans = [];
    let count = 0;
    for(let i=0; i<arr.length; i++) {
      arr[i].then(val => {
        ans[i] = { status: 'fulfilled', value: val };
      }, err => {
        ans[i] = { status: 'rejected', reason: err };
      }).finally(() => {
        count++;
        if (count === arr.length) {
          resolve(ans);
        }
      });
    }
  });
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 冒泡排序

for(let i=1; i<arr.length; i++) {
  for(let j=0; j<arr.length-i; j++) {
    if (arr[j] > arr[j+1]) {
      [arr[j], arr[j+1]] = [arr[j+1], arr[j]];
    }
  }
}
1
2
3
4
5
6
7

# 选择排序

for(let i=0; i<arr.length; i++) {
  let minIndex = i;
  for(let j=i+1; j<arr.length; j++) {
    if (arr[minIndex] > arr[j]) {
      minIndex = j;
    }
  }
  [arr[minIndex], arr[j]] = [arr[j], arr[minIndex]];
}
1
2
3
4
5
6
7
8
9

# 插入排序

function inserttest(arr) {
    for(let i=1; i<arr.length; i++) {
        let curIndex = i;
        while(curIndex>0 && arr[curIndex - 1] > arr[currIndex]) {
            [arr[curIndex-1], arr[curIndex]] = [arr[curIndex], arr[curIndex-1]]
            curIndex--;
        }
    }
}
1
2
3
4
5
6
7
8
9

# 快速排序

function quickSort(arr) {
    if (arr.length < 2) return arr;
    let base = arr[0];
    let left = [];
    let right = [];
    for(let i=0; i<arr.length; i++) {
        if (arr[i] < base) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    return quickSort(left).concat(base).concat(quickSort(right));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14