# 手写代码无敌秘籍(一)

# 手写一个 new 操作符

function New(func) {
  var res = {};
  if (func.prototype !== null) {
    res.__proto__ = func.prototype;
  }
  var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
  if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
    return ret;
  }
  return res;
}
1
2
3
4
5
6
7
8
9
10
11

new 操作符做了这些事

  • 它创建了一个全新的对象
  • 它会被执行[[Prototype]] 也就是 proto 链接
  • 它使 this 指向新创建的对象
  • 通过 new 创建的每个对象将最终被[[Prototype]]链接到这个函数的 prototype 对象上
  • 如果函数没有返回对象类型 Object,那么 new 表达式中的函数调用将返回该对象

# 手写一个 JSON.stringify 和 JSON.parse

# 手写一个 call 或 apply

call

Function.prototype.call = function(context = window) {
  context.fn = this;
  let args = [...arguments].slice(1);
  let result = context.fn(...args);
  delete context.fn;
  return result;
};
1
2
3
4
5
6
7

apply

Function.prototype.apply = function(context = window) {
  context.fn = this;
  let result;
  if (arguments[1]) {
    result = context.fn(...arguments[1]);
  } else {
    result = context.fn();
  }
  delete context.fn;
  return result;
};
1
2
3
4
5
6
7
8
9
10
11

# 手写一个 Function.bind

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== "function") {
      throw new TypeError(
        "Function.prototype.bind - " +
          "what is trying to be bound is not callable"
      );
    }
    var aArgs = Array.prototype.slice.call(arguments, 1),
      fToBind = this,
      fNOP = function() {},
      fBound = function() {
        // this instanceof fBound === true 时,说明返回的fBound被当做new的构造函数调用
        return fToBind.apply(
          this instanceof fBound ? this : oThis,
          // 获取调用时(fBound)的传参,bind返回的函数入参往往是这么传递的
          aArgs.concat(Array.prototype.slice.call(arguments))
        );
      };
    // 维护原型关系
    if (this.prototype) {
      // 当执行Function.prototype.bind()时。this为Function.prototype
      // this.prototype(即Function.prototype.prototype)为undefined
      fNOP.prototype = this.prototype;
    }
    // 下行的代码使fBound.prototype是fNOP的实例,因此
    // 返回的fBound若作为new的构造函数,new生成的新对象作为this传入fBound,新对象的__proto__就是fNOP的实例
    fBound.prototype = new fNOP();
    return fBound;
  };
}
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

换一种继承的方法

if (!Function.prototype.bind) {
  Function.prototype.bind = function(oThis) {
    if (typeof this !== "function") {
      throw new TypeError(
        "Function.prototype.bind - " +
          "what is trying to be bound is not callable"
      );
    }
    var aArgs = Array.prototype.slice.call(arguments, 1),
      fToBind = this,
      fBound = function() {
        // this instanceof fBound === true 时,说明返回的fBound被当做new的构造函数调用
        return fToBind.apply(
          this instanceof fBound ? this : oThis,
          // 获取调用时(fBound)的传参,bind返回的函数入参往往是这么传递的
          aArgs.concat(Array.prototype.slice.call(arguments))
        );
      };
    // 继承
    fBound.prototype = Object.create(this.prototype, {
      constructor: {
        value: fBound
      }
    });
    return fBound;
  };
}
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 Father() {
  this.name = "father";
  this.age = 40;
}

Father.prototype.say = function() {
  console.log(111);
};

function Son() {
  Father.call(this);
  this.age = 18;
}

function create(o) {
  var Fun = function() {};
  Fun.prototype = o.prototype;
  return new Fun();
}

Son.prototype = create(Father);
Son.prototype.construct = Son;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

老董绝招

function Father() {
  this.name = "father";
  this.age = 40;
}

Father.prototype.say = function() {
  console.log(111);
};

function Son() {
  Father.call(this);
  this.age = 18;
}

Son.prototype = Object.create(Father.prototype, {
  constructor: {
    value: Son
  }
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 手写一个 JS 函数柯里化和反柯里化

const curry = fn => {
  const len = fn.length;
  return function curried(...args) {
    if (args.length === len) {
      return fn.apply(null, args);
    }
    return (..._args) => {
      return curried.apply(null, [...args, ..._args]);
    };
  };
};

const sum = (x, y, z) => x + y + z;
const add = curry(sum);

// 6
add(1, 2, 3);

// 6
add(1, 2)(3);

// 6
add(1)(2, 3);

// 6
add(1)(2)(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
const add = function(...args) {
  let sum = args.reduce((a, b) => a + b, 0);
  let temp = function(..._args) {
    sum = _args.reduce((a, b) => a + b, sum);
    return temp;
  };
  temp.toString = function() {
    return sum;
  };
  return temp;
};

console.log(add(1)); //1
console.log(add(1)(2)); //3
console.log(add(1)(2)(3)); //6
console.log(add(1, 2)(3)); //6
console.log(add(1)(2, 3)); //6
console.log(add(1)(2)(3)(4)); //10
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 手写一个 Promise

// 三种状态
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
// promise 接收一个函数参数,该函数会立即执行
function MyPromise(fn) {
  let _this = this;
  _this.currentState = PENDING;
  _this.value = undefined;
  // 用于保存 then 中的回调,只有当 promise
  // 状态为 pending 时才会缓存,并且每个实例至多缓存一个
  _this.resolvedCallbacks = [];
  _this.rejectedCallbacks = [];

  _this.resolve = function(value) {
    if (value instanceof MyPromise) {
      // 如果 value 是个 Promise,递归执行
      return value.then(_this.resolve, _this.reject);
    }
    setTimeout(() => {
      // 异步执行,保证执行顺序
      if (_this.currentState === PENDING) {
        _this.currentState = RESOLVED;
        _this.value = value;
        _this.resolvedCallbacks.forEach(cb => cb());
      }
    });
  };

  _this.reject = function(reason) {
    setTimeout(() => {
      // 异步执行,保证执行顺序
      if (_this.currentState === PENDING) {
        _this.currentState = REJECTED;
        _this.value = reason;
        _this.rejectedCallbacks.forEach(cb => cb());
      }
    });
  };
  // 用于解决以下问题
  // new Promise(() => throw Error('error))
  try {
    fn(_this.resolve, _this.reject);
  } catch (e) {
    _this.reject(e);
  }
}

MyPromise.prototype.then = function(onResolved, onRejected) {
  var self = this;
  // 规范 2.2.7,then 必须返回一个新的 promise
  var promise2;
  // 规范 2.2.onResolved 和 onRejected 都为可选参数
  // 如果类型不是函数需要忽略,同时也实现了透传
  // Promise.resolve(4).then().then((value) => console.log(value))
  onResolved = typeof onResolved === "function" ? onResolved : v => v;
  onRejected = typeof onRejected === "function" ? onRejected : r => throw r;

  if (self.currentState === RESOLVED) {
    return (promise2 = new MyPromise(function(resolve, reject) {
      // 规范 2.2.4,保证 onFulfilled,onRjected 异步执行
      // 所以用了 setTimeout 包裹下
      setTimeout(function() {
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      });
    }));
  }

  if (self.currentState === REJECTED) {
    return (promise2 = new MyPromise(function(resolve, reject) {
      setTimeout(function() {
        // 异步执行onRejected
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (reason) {
          reject(reason);
        }
      });
    }));
  }

  if (self.currentState === PENDING) {
    return (promise2 = new MyPromise(function(resolve, reject) {
      self.resolvedCallbacks.push(function() {
        // 考虑到可能会有报错,所以使用 try/catch 包裹
        try {
          var x = onResolved(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (r) {
          reject(r);
        }
      });

      self.rejectedCallbacks.push(function() {
        try {
          var x = onRejected(self.value);
          resolutionProcedure(promise2, x, resolve, reject);
        } catch (r) {
          reject(r);
        }
      });
    }));
  }
};
// 规范 2.3
function resolutionProcedure(promise2, x, resolve, reject) {
  // 规范 2.3.1,x 不能和 promise2 相同,避免循环引用
  if (promise2 === x) {
    return reject(new TypeError("Error"));
  }
  // 规范 2.3.2
  // 如果 x 为 Promise,状态为 pending 需要继续等待否则执行
  if (x instanceof MyPromise) {
    if (x.currentState === PENDING) {
      x.then(function(value) {
        // 再次调用该函数是为了确认 x resolve 的
        // 参数是什么类型,如果是基本类型就再次 resolve
        // 把值传给下个 then
        resolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
    return;
  }
  // 规范 2.3.3.3.3
  // reject 或者 resolve 其中一个执行过得话,忽略其他的
  let called = false;
  // 规范 2.3.3,判断 x 是否为对象或者函数
  if (x !== null && (typeof x === "object" || typeof x === "function")) {
    // 规范 2.3.3.2,如果不能取出 then,就 reject
    try {
      // 规范 2.3.3.1
      let then = x.then;
      // 如果 then 是函数,调用 x.then
      if (typeof then === "function") {
        // 规范 2.3.3.3
        then.call(
          x,
          y => {
            if (called) return;
            called = true;
            // 规范 2.3.3.3.1
            resolutionProcedure(promise2, y, resolve, reject);
          },
          e => {
            if (called) return;
            called = true;
            reject(e);
          }
        );
      } else {
        // 规范 2.3.3.4
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    // 规范 2.3.4,x 为基本类型
    resolve(x);
  }
}
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171

# 手写防抖和节流

防抖

function debounce(fn, wait = 50, immediate) {
  let timer;
  return function() {
    if (immediate) {
      fn.apply(this, arguments);
    }
    if (timer) {
      clearTimeout(timer);
    }
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, wait);
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

节流

function throttle(fn, wait) {
  let pre = new Date();
  return function() {
    let now = new Date();
    if (now - pre > wait) {
      fn.apply(this, arguments);
      pre = new Date();
    }
  };
}
1
2
3
4
5
6
7
8
9
10

# 手写一个 js 深拷贝(由浅入深多种解法)

丐中丐版

let co = JSON.parse(JSON.stringify(o));
1

基础版本

function deepCopy(obj) {
  if (typeof obj === "object") {
    if (obj.constructor === Array) {
      var newArr = [];
      for (var i = 0; i < obj.length; i++) newArr.push(obj[i]);
      return newArr;
    } else {
      var newObj = {};
      for (var key in obj) {
        newObj[key] = this.deepCopy(obj[key]);
      }
      return newObj;
    }
  } else {
    return obj;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

必杀!!!

const isComplexDataType = obj =>
  (typeof obj === "object" || typeof obj === "function") && obj !== null;

const deepClone = function(obj, hash = new WeakMap()) {
  // 如果成环了,参数obj = obj.loop = 最初的obj
  // 会在WeakMap中找到第一次放入的obj提前返回第一次放入WeakMap的cloneObj
  if (hash.has(obj)) return hash.get(obj);
  let type = [Date, RegExp, Set, Map, WeakMap, WeakSet];
  if (type.includes(obj.constructor)) return new obj.constructor(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)) {
    //Reflect.ownKeys(obj)可以拷贝不可枚举属性和符号类型
    // 如果值是引用类型(非函数)则递归调用deepClone
    cloneObj[key] = isComplexDataType(obj[key])
      ? deepClone(obj[key], hash)
      : obj[key];
  }
  return cloneObj;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

测试用例

let obj = {
  bigInt: BigInt(12312),
  set: new Set([2]),
  map: new Map([["a", 22], ["b", 33]]),
  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;

let cloneObj = deepClone(obj);

console.log("obj", obj);
console.log("cloneObj", cloneObj);

for (let key of Object.keys(cloneObj)) {
  if (
    typeof cloneObj[key] === "object" ||
    typeof cloneObj[key] === "function"
  ) {
    console.log(`${key}相同吗? `, cloneObj[key] === obj[key]);
  }
}
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

# 手写一个 instanceOf 原理

function instanceOf(left, right) {
  let proto = left.__proto__;
  let prototype = right.prototype;
  while (true) {
    if (proto === null) {
      return false;
    }
    if (proto === prototype) {
      return true;
    }
    proto = proto.__proto__;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 手写一个 map 和 reduce

# 手写实现拖拽

# 使用 setTimeout 模拟 setInterval

const s = function() {
  console.log(1);
  setTimeout(s, 1000);
};
1
2
3
4

# 手写实现 Object.create 的基本原理

# 手写实现一个基本的 Event Bus