手写new和bind

# 手写new和bind

new 运算符号

运算符创建一个用户定义的对象类型的实例或具有构造函数的内置对象类型之一

bind()

方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )

/**
 *
 * @param {*} context
 * @returns
 */

Function.prototype.myBind = function (context) {
  // bind是函数方法,调用校验
  if (typeof this !== 'function') return '必须要函数才可以调用'
  let fn = this

  // 处理bind函数里面的参数
  // let bindArg = [...arguments].slice(1);
  const bindArg = Array.prototype.slice.call(arguments, 1)

  // 需要一个空函数来周转
  const fNOP = function () {}
  // 也就是说当 bind 返回的函数作为构造函数的时候 被new了,bind 时指定的 this 值会失效,但传入的参数依然生效。
  // 举个例子:
  // 比如new了之后 肯定指向实例 而不是bind一开始指向的obj了
  const fBound = function () {
    // 剩余的参数
    let arg = Array.prototype.slice.call(arguments)
    // const bindArgs = Array.prototype.slice.call(arguments)
    // const finalArgs = args.concat(bindArgs)

    return fn.apply(this instanceof fBound ? this : context, [
      ...bindArg,
      ...arg,
    ])
  }

  // 这样就可以访问Person函数原型链中的值了,但是bind里面并没有改变person的prototype
  // 因为我们模拟的是一种polyfill的函数,和es5里面的bind并不完全相等,polyfill是为了兼容老的浏览器所以创造的写法
  // 不是我们内置的函数,所以无法创建一个不含.prototype的bind函数
  // 因此会有些副作用,所以我们在new中使用硬绑定函数并且使用polyfill函数要小心

  fNOP.prototype = this.prototype;
  // fBound.prototype = Object.create(this.prototype);
  fBound.prototype = new fNOP();
  return fBound;
}

/**
 *
 * @param {*} Constructor
 * @param  {...any} args
 * @returns
 *
 * new
 * 生成一个新的对象
 * 新对象的_proto_指向构造函数的prototype
 * 改变构造函数的this指向,使其指向新对象,这样以来obj就有构造函数里面的属性啦
 * 判断构造函数是否有返回值,如果构造函数返回对象,则直接返回构造函数中的对象
 * 返回新对象
 *
 */

const createInstance = (Constructor, ...args) => {
  // Object.getPrototypeOf(obj)相当于obj.__proto__
  const instance = Object.create(Constructor.prototype)
  // 构造函数内部的 this 指向 instance 变量
  let res = Constructor.call(instance, ...args)
  const isObj = res !== null && typeof res === 'object'
  const isFun = typeof res === 'function'
  return isFun || isObj ? res : instance
}

// new实现如下:
function create() {
 // 获取构造函数,同时删除arguments中第一个参数
 Con = [].shift.call(arguments);
 // 创建一个空的对象并链接到原型,obj可以访问构造函数原型中的属性
 var obj = Object.create(Con.prototype);
 // 绑定this实现继承,obj可以访问到构造函数中的属性
 var result = Con.apply(obj, arguments);
 // 优先返回构造函数返回的对象
 return result instanceof Object ? result : obj;
}
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
上次更新: 2022/7/6 下午8:56:46