# 基础手写

# 字符串相关

/* 
1. 字符串倒序: reverseString(str)  生成一个倒序的字符串
*/
export function reverseString(str) {
	// return str.split('').reverse().join('')
    // return [...str].reverse().join('')
    return Array.from(str).reverse().join('')
}

/* 
2. 字符串是否是回文: palindrome(str) 如果给定的字符串是回文,则返回 true ;否则返回 false
*/
export function palindrome(str) {
    return str === reverseString(str)
}

/* 
3. 截取字符串: truncate(str, num) 如果字符串的长度超过了num, 截取前面num长度部分, 并以...结束
*/
export function truncate(str, num) {
	return str.length > num ? str.slice(0, num) + '...' : str
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 数据类型判断

typeof 可以正确识别:Undefined、Boolean、Number、String、Symbol、Function 等类型的数据,但是对于其他的都会认为是 object,比如 Null、Date 等,所以通过 typeof 来判断数据类型会不准确。

function getType(obj){

  let type  = typeof obj;

  if (type !== "object") {    // 先进行typeof判断,如果是基础数据类型,直接返回

    return type;

  }

  // 对于typeof返回结果是object的,再进行如下的判断,正则返回结果

  return Object.prototype.toString.call(obj).replace(/^\[object (\S+)\]$/, '$1');  // 注意正则中间有个空格

}

/* 代码验证,需要注意大小写,哪些是typeof判断,哪些是toString判断?思考下 */

getType([])     // "Array" typeof []是object,因此toString返回

getType('123')  // "string" typeof 直接返回

getType(window) // "Window" toString返回

getType(null)   // "Null"首字母大写,typeof null是object,需toString来判断

getType(undefined)   // "undefined" typeof 直接返回

getType()            // "undefined" typeof 直接返回

getType(function(){}) // "function" typeof能判断,因此首字母小写

getType(/123/g)      //"RegExp" toString返回
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

# 类型转换

# 1.隐式类型转换

null == undefined       // true  规则2

null == 0               // false 规则2

'' == null              // false 规则2

'' == 0                 // true  规则4 字符串转隐式转换成Number之后再对比

'123' == 123            // true  规则4 字符串转隐式转换成Number之后再对比

0 == false              // true  e规则 布尔型隐式转换成Number之后再对比

1 == true               // true  e规则 布尔型隐式转换成Number之后再对比

var a = {

  value: 0,

  valueOf: function() {

    this.value++;

    return this.value;

  }

};

// 注意这里a又可以等于1、2、3

console.log(a == 1 && a == 2 && a ==3);  //true f规则 Object隐式转换

// 注:但是执行过3遍之后,再重新执行a==3或之前的数字就是false,因为value已经加上去了,这里需要注意一下
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

# 2.Object类型转换

var obj = {

  value: 1,

  valueOf() {

    return 2;

  },

  toString() {

    return '3'

  },

  [Symbol.toPrimitive]() {

    return 4

  }

}

console.log(obj + 1); // 输出5

// 因为有Symbol.toPrimitive,就优先执行这个;如果Symbol.toPrimitive这段代码删掉,则执行valueOf打印结果为3;如果valueOf也去掉,则调用toString返回'31'(字符串拼接)

// 再看两个特殊的case:

10 + {}

// "10[object Object]",注意:{}会默认调用valueOf是{},不是基础类型继续转换,调用toString,返回结果"[object Object]",于是和10进行'+'运算,按照字符串拼接规则来,参考'+'的规则C

[1,2,undefined,4,5] + 10

// "1,2,,4,510",注意[1,2,undefined,4,5]会默认先调用valueOf结果还是这个数组,不是基础数据类型继续转换,也还是调用toString,返回"1,2,,4,5",然后再和10进行运算,还是按照字符串拼接规则,参考'+'的第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
28
29
30
31
32
33
34
35
36
37

# 深拷贝

//浅拷贝:只考虑对象类型。
function shallowCopy(obj) {
  if (typeof obj !== "object") return;
  let newObj = obj instanceof Array ? [] : {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = obj[key];
    }
  }
  return newObj;
}
//简单版深拷贝:只考虑普通对象属性,不考虑内置对象和函数。
function deepClone(obj) {
  if (typeof obj !== "object") return;
  var newObj = obj instanceof Array ? [] : {};
  for (var key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] =
        typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
    }
  }
  return newObj;
}
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)

const deepClone = function (obj, hash = new WeakMap()) {

  if (obj.constructor === Date) 

  return new Date(obj)       // 日期对象直接返回一个新的日期对象

  if (obj.constructor === RegExp)

  return new RegExp(obj)     //正则对象直接返回一个新的正则对象

  //如果循环引用了就用 weakMap 来解决

  if (hash.has(obj)) return hash.get(obj)

  let allDesc = Object.getOwnPropertyDescriptors(obj)

  //遍历传入参数所有键的特性

  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)

  //继承原型链

  hash.set(obj, cloneObj)

  for (let key of Reflect.ownKeys(obj)) { 

    cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]

  }

  return cloneObj

}

// 下面是验证代码

let obj = {

  num: 0,

  str: '',

  boolean: true,

  unf: undefined,

  nul: null,

  obj: { name: '我是一个对象', id: 1 },

  arr: [0, 1, 2],

  func: function () { console.log('我是一个函数') },

  date: new Date(0),

  reg: new RegExp('/我是一个正则/ig'),

  [Symbol('1')]: 1,

};

Object.defineProperty(obj, 'innumerable', {

  enumerable: false, value: '不可枚举属性' }

);

obj = Object.create(obj, Object.getOwnPropertyDescriptors(obj))

obj.loop = obj    // 设置loop成循环引用的属性

let cloneObj = deepClone(obj)

cloneObj.arr.push(4)

console.log('obj', obj)

console.log('cloneObj', cloneObj)
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

# 继承

# 1.原型继承

  function Parent1() {

    this.name = 'parent1';

    this.play = [1, 2, 3]

  }

  function Child1() {

    this.type = 'child2';

  }

  Child1.prototype = new Parent1();

  console.log(new Child1());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 2.构造函数继承 (call)

  function Parent1(){

    this.name = 'parent1';

  }



  Parent1.prototype.getName = function () {

    return this.name;

  }



  function Child1(){

    Parent1.call(this);

    this.type = 'child1'

  }



  let child = new Child1();

  console.log(child);  // 没问题

  console.log(child.getName());  // 会报错
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

# 3.组合继承(原型继承+构造函数继承)

  function Parent3 () {

    this.name = 'parent3';

    this.play = [1, 2, 3];

  }



  Parent3.prototype.getName = function () {

    return this.name;

  }

  function Child3() {

    // 第二次调用 Parent3()

    Parent3.call(this);

    this.type = 'child3';

  }



  // 第一次调用 Parent3()

  Child3.prototype = new Parent3();

  // 手动挂上构造器,指向自己的构造函数

  Child3.prototype.constructor = Child3;

  var s3 = new Child3();

  var s4 = new Child3();

  s3.play.push(4);

  console.log(s3.play, s4.play);  // 不互相影响

  console.log(s3.getName()); // 正常输出'parent3'

  console.log(s4.getName()); // 正常输出'parent3'
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

# 4.原型式继承(Object.create)

  let parent4 = {

    name: "parent4",

    friends: ["p1", "p2", "p3"],

    getName: function() {

      return this.name;

    }

  };



  let person4 = Object.create(parent4);

  person4.name = "tom";

  person4.friends.push("jerry");



  let person5 = Object.create(parent4);

  person5.friends.push("lucy");



  console.log(person4.name);

  console.log(person4.name === person4.getName());

  console.log(person5.name);

  console.log(person4.friends);

  console.log(person5.friends);
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

# 5.寄生式继承

   let parent5 = {

    name: "parent5",

    friends: ["p1", "p2", "p3"],

    getName: function() {

      return this.name;

    }

  };



  function clone(original) {

    let clone = Object.create(original);

    clone.getFriends = function() {

      return this.friends;

    };

    return clone;

  }



  let person5 = clone(parent5);



  console.log(person5.getName());

  console.log(person5.getFriends());
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

# 6.寄生组合式继承(类似extends)

  function clone (parent, child) {

    // 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程

    child.prototype = Object.create(parent.prototype);

    child.prototype.constructor = child;

  }



  function Parent6() {

    this.name = 'parent6';

    this.play = [1, 2, 3];

  }

   Parent6.prototype.getName = function () {

    return this.name;

  }

  function Child6() {

    Parent6.call(this);

    this.friends = 'child5';

  }



  clone(Parent6, Child6);



  Child6.prototype.getFriends = function () {

    return this.friends;

  }



  let person6 = new Child6();

  console.log(person6);

  console.log(person6.getName());

  console.log(person6.getFriends());
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

# 7.ES6 entends继承

class Person {

  constructor(name) {

    this.name = name

  }

  // 原型方法

  // 即 Person.prototype.getName = function() { }

  // 下面可以简写为 getName() {...}

  getName = function () {

    console.log('Person:', this.name)

  }

}

class Gamer extends Person {

  constructor(name, age) {

    // 子类中存在构造函数,则需要在使用“this”之前首先调用 super()。

    super(name)

    this.age = age

  }

}

const asuna = new Gamer('Asuna', 20)

asuna.getName() // 成功访问到父类的方法
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

img

# 发布订阅模式/事件总线

class EventEmitter {
  constructor() {
    // handlers是一个map,用于存储事件与回调之间的对应关系
    this.handlers = {}
  }

  // on方法用于安装事件监听器,它接受目标事件名和回调函数作为参数
  on(eventName, cb) {
    // 先检查一下目标事件名有没有对应的监听函数队列
    if (!this.handlers[eventName]) {
      // 如果没有,那么首先初始化一个监听函数队列
      this.handlers[eventName] = []
    }

    // 把回调函数推入目标事件的监听函数队列里去
    this.handlers[eventName].push(cb)
  }

  // emit方法用于触发目标事件,它接受事件名和监听函数入参作为参数
  emit(eventName, ...args) {
    // 检查目标事件是否有监听函数队列
    if (this.handlers[eventName]) {
      // 这里需要对 this.handlers[eventName] 做一次浅拷贝,主要目的是为了避免通过 once 安装的监听器在移除的过程中出现顺序问题
      const handlers = this.handlers[eventName].slice()
      // 如果有,则逐个调用队列里的回调函数
      handlers.forEach((callback) => {
        callback(...args)
      })
    }
  }

  // 移除某个事件回调队列里的指定回调函数
  off(eventName, cb) {
    const callbacks = this.handlers[eventName]
    const index = callbacks.indexOf(cb)
    if (index !== -1) {
      callbacks.splice(index, 1)
    }
  }

  // 为事件注册单次监听器
  once(eventName, cb) {
    // 对回调函数进行包装,使其执行完毕自动被移除
    const wrapper = (...args) => {
      cb(...args)
      this.off(eventName, wrapper)
    }
    this.on(eventName, wrapper)
  }
}
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

# call/apply/bind

# 1.call

Function.prototype.myCall = function (obj, ...arg) {
    const context = obj || window;
    const fn = Symbol(); // 保证唯一
    context[fn] = this;
    const res = context[fn](...arg);
    delete context[fn];
    return res;
}
1
2
3
4
5
6
7
8

# 2.apply

Function.prototype.myApply = function (obj, arg) {
    const context = obj || window;
    const fn = Symbol(); // 保证唯一
    context[fn] = this; 
    const res = context[fn](...arg);
    delete context[fn];
    return res;
}
1
2
3
4
5
6
7
8

# 3.bind

Function.prototype.bind = function (obj, ...args) {
  const context = obj || window;
  const fn = Symbol()
  context[fn] = this;
  let that = this;
 
  const res = function(...others){ 
  		return that.apply(
      	this instanceof that ? this : context, //new时, this指向res的实例,res继承自that
        [...args,...others]
      )
  }
  
  // 如果绑定的是构造函数 那么需要继承构造函数原型属性和方法
  res.prototype = Object.create(that.prototype)
  
  return res 
}

// 使用
function Point(x, y) {
    this.x = x;
    this.y = y;
}

Point.prototype.toString = function () {
    return this.x + ',' + this.y;
}

let YPoint = Point.bind(null, 1);
let axiosPoint = new YPoint(2);

console.log(axiosPoint.toString()) // '1,2'
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

https://www.jianshu.com/p/b540e1e17f54

# 数组基本方法

  • map(): 返回一个由回调函数的返回值组成的新数组
  • filter(): 将所有在过滤函数中返回true的数组元素放进一个新数组中并返回
  • find(): 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回undefined。
  • findIndex(): 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回-1。
  • every(): 如果数组中的每个元素都满足测试函数,则返回true,否则返回false。
  • some(): 如果数组中至少有一个元素满足测试函数,则返回 true,否则返回 false。
  • reduce(): 从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最后一次回调函数的返回值
/* 
实现map()
*/
export function map (array, callback) {
  const arr = []
  for (let i = 0; i < array.length; i++) {
    arr.push(callback(array[i], i))
  }
  return arr
}


/* 
实现filter()
*/
export function filter(array, callback) {
  const arr = []
  for (let i = 0; i < array.length; i++) {
    if (callback(array[i], i)) {
      arr.push(array[i])
    }
  }
  return arr
}

/* 
实现find()
*/
export function find (array, callback) {
  for (let i = 0; i < array.length; i++) {
    if (callback(array[i], i)) {
      return array[i]
    }
  }
  return undefined
}


/* 
实现findIndex()
*/
export function findIndex (array, callback) {
  for (let i = 0; i < array.length; i++) {
    if (callback(array[i], i)) {
      return i
    }
  }
  return -1
}

 实现every()
 */
 export function every (array, callback) {
  for (let i = 0; i < array.length; i++) {
    if (!callback(array[i], i)) { // 只有一个结果为false, 直接返回false
      return false
    }
  }
  return true
}

/* 
实现some()
*/
export function some (array, callback) {
  for (let i = 0; i < array.length; i++) {
    if (callback(array[i], i)) { // 只有一个结果为true, 直接返回true
      return true
    }
  }
  return false
}

/* 
	reduce()
*/
function reduce(array, callback, initValue) {
  let acc = initValue;
  let startAtIndex = 0;

  if (initValue === undefined) {
    acc = array[0];
    startAtIndex = 1;
  }

  for (let i = startAtIndex; i < array.length; i++) {
    acc = callback(acc, array[i], i, array)
  }
 
  return acc;
}
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

# 数组去重

  • 根据当前数组产生一个去除重复元素后的新数组

  • 如: [2, 3, 2, 7, 6, 7] ==> [2, 3, 7, 6]

  • 方法1: 利用forEach()和indexOf() ○说明: 本质是双重遍历, 效率差些

  • 方法2: 利用forEach() + 对象容器 ○说明: 只需一重遍历, 效率高些

  • 方法3: 利用ES6语法: from + Set 或者 ... + Set ○说明: 编码简洁

/*
方法1: 利用forEach()和indexOf()
  说明: 本质是双重遍历, 效率差些
*/
export function unique1 (array) {
  const arr = []
  array.forEach(item => {
    if (arr.indexOf(item) === -1) {
      arr.push(item)
    }
  })
  return arr
}

/*
方法2: 利用forEach() + 对象容器
  说明: 只需一重遍历, 效率高些
*/
export function unique2 (array) {
  const arr = []
  const obj = {}
  array.forEach(item => {
    if (!obj.hasOwnProperty(item)) {
      obj[item] = true
      arr.push(item)
    }
  })
  return arr
}

/*
方法3: 利用ES6语法
    1). from + Set
    2). ... + Set
    说明: 编码简洁
*/
export function unique3 (array) {
  // return Array.from(new Set(array))
  return [...new Set(array)]
}
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

# 数组扁平化

  • 语法: flatten(array)
  • 取出嵌套数组(多维)中的所有元素放到一个新数组(一维)中
  • 如: [1, [3, [2, 4]]] ==> [1, 3, 2, 4]

ES2019 方法 flat(depth) 深度,默认值为 1。

  • flat() 方法会移除数组中的空项
  • 使用 Infinity,可展开任意深度的嵌套数组
var arr = [1, 2, [3, 4]];

// 展开一层数组
arr.flat();
// 等效于
arr.reduce((acc, val) => acc.concat(val), []);
// [1, 2, 3, 4]

// 使用扩展运算符 ...
const flattened = arr => [].concat(...arr);
// 方法1:ES6中 Array.prototype.flat()
console.log(arr.flat(Infinity))


// 方法2:迭代的思路
function flat(arr, depth = 1) {
  while (arr.some((item) => Array.isArray(item)) && depth > 0) {
    arr = [].concat(...arr);
    depth--;
  }
  return arr;
}


// 方法3:递归
function flat(arr, depth = 1) {
  let result = [];
  function _loop(arr) {
    arr.forEach((item) => {
      if (Array.isArray(item) && depth > 0) {
        _loop(item, depth--);
      } else {
        result.push(item);
      }
    });
  }
  _loop(arr);
  return result;
}



// 方法4:reduce + 递归
function flat(arr, depth = 1) {
  return depth > 0
    ? arr.reduce(
        (acc, val) =>
          acc.concat(Array.isArray(val) ? flat(val, depth - 1) : val),
        []
      )
    : arr.slice();
}



// toString
function flat (arr) {
  return arr.toString().split(',').map(item => Number(item)) //转成字符串会只有一个逗号相隔
}
console.log(flat(arr))

// stringify
function flat (arr) {
  let str = JSON.stringify(arr)
  str = str.replace(/\[|\]/g, '') //去掉中括号
  return str.split(',').map(item => Number(item)))
}
console.log(flat(arr))
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

# 节流/防抖

# 1.节流throttle

//🔥时间戳,缺点:第一次会执行,最后少一次
function throttle1(fn, timeout) {
  let last = 0;
  return function () {
    let now = Date.now();
    if (now - last >= timeout) {
      last = now;
      fn.apply(this, arguments); // this是dom,args 第一个是event事件
    }
  };
}


//🔥定时器,缺点:最后一次也会等一个时间间隔
function throttle2(fn, timeout) {
  let timer = null;
  return function () {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, arguments);
        timer = null;
      }, timeout);
    }
  };
}


// 结合
function throttle3(fn, timeout) {
  let timer = null;
  let last = 0;
  return function () {
    let now = Date.now();
    let remaining = timeout - (now - last);
    if (remaining <= 0) {
      fn.apply(this, arguments);
      last = now;
    } else if(!timer) {
      timer = setTimeout(() => {
        fn.apply(this, arguments);
        last = Date.now();     
        timer = null
      }, remaining);
    }
  };
}
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

# 2.防抖debounce

//🔥初级版本 第一次执行延时
function debounce1(fn,wait){
  let timer = null;
  return function(){
   if(timer){
     clearTimeout(timer);
   }
   timer=setTimeout(()=>{
    fn.apply(this, arguments);
   },wait);
  }
}


// 🔥反过来 第一次执行
function debounce2(fn, timeout) {
  let timer = null;
  return function () {
    if (timer) {
      clearTimeout(timer);
    }
    if (!timer) {
      fn.apply(this, arguments);
    }
    timer = setTimeout(() => {
      timer = null;
    }, timeout);
  };
}
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

# 原型相关

# 1.new

  • 语法: newInstance(Fn, ...args)
  • 功能: 创建 Fn 构造函数的实例对象
function myNew(Fn, ...args) {
	const obj = {};
  // =  Object.setPrototypeOf(obj, Fn.prototype);
  obj.__proto__ = Fn.prototype;
	const result = Fn.apply(obj, args)
  // 如果Fn返回的是一个对象类型, 那返回的就不再是obj, 而是Fn返回的对象
	return (typeof result === 'object' && result != null) ? result : obj
  // 最后一句也可以  return result instanceof Object ? result : obj
}
1
2
3
4
5
6
7
8
9

# 2.instanceof

  • 语法: myInstanceOf(obj, Type)
  • 功能: 判断 ob j是否是 Type 类型的实例
  • 实现: Type 的原型对象是否是 obj 的原型链上的某个对象, 如果是返回 true, 否则返回 false
function myInstanceof(left, right) {

  // 这里先用typeof来判断基础数据类型,如果是,直接返回false

  if(typeof left !== 'object' || left === null) return false;

  // getProtypeOf是Object对象自带的API,能够拿到参数的原型对象

  let proto = Object.getPrototypeOf(left);

  while(true) {                  //循环往下寻找,直到找到相同的原型对象

    if(proto === null) return false;

    if(proto === right.prototype) return true;//找到相同原型对象,返回true

    proto = Object.getPrototypeof(proto);

    }

}

// 验证一下自己实现的myInstanceof是否OK

console.log(myInstanceof(new Number(123), Number));    // true

console.log(myInstanceof(123, Number));                // false
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

# 3.Object.create

Object.create() 方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__

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

使用:

	// 子类继承父类的原型属性
	Worker.prototype = Object.create(Person.prototype)
	Worker.prototype.constructor = Worker
1
2
3

# 异步控制并发数

function limitRequest(urls = [], limit = 3) {
  return new Promise((resolve, reject) => {
    const len = urls.length
    let count = 0
    // Start limit tasks simultaneously
    while (limit > 0) {
      start()
      limit -= 1
    }
    function start() {
      const url = urls.shift() // Take the first task from the array
      if (url) {
        axios.post(url).finally(() => {
          if (count == len - 1) {
            // The last task is completed
            resolve()
          } else {
            // After completion, start the next task
            count++
            start()
          }
        })
      }
    }
  })
}
// test
limitRequest(['http://xxa','http://xxb','http://xxc','http://xxd','http://xxe'])
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

# 获取url参数

// URLSearchParams 方法

// Create an instance of URLSearchParams
const urlSearchParams = new URLSearchParams(window.location.search);
// Convert the list of key-value pairs into an object
const params = Object.fromEntries(urlSearchParams.entries());




// split 方法

function getParams(url) {
  const res = {}
  if (url.includes('?')) {
    const str = url.split('?')[1]
    const arr = str.split('&')
    arr.forEach(item => {
      const key = item.split('=')[0]
      const val = item.split('=')[1]
      res[key] = decodeURIComponent(val) // decode
    })
  }
  return res
}
// test
const user = getParams('http://www.baidu.com?user=%E9%98%BF%E9%A3%9E&age=16')
console.log(user) // {user:'abor', age: '16'}
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

# 函数柯里化

function sumFn(a,b,c){return a+ b + c};
let sum = curry(sumFn);
sum(2)(3)(5)//10
sum(2,3)(5)//10
function curry(fn,...args){
  let fnLen = fn.length,
      argsLen = args.length;
  //对比函数的参数和当前传入参数
  //若参数不够就继续递归返回curry
  //若参数够就调用函数返回相应的值
  if(fnLen > argsLen){
    return function(...arg2s){
      return curry(fn,...args,...arg2s)
    }
  }else{
    return fn(...args)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

其他参考链接 (opens new window)