1.实现 call apply

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数语法:function.call(thisArg, arg1, arg2, …)

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
//实现apply只要把下一行中的...args换成args即可

Function.prototype.myCall = function (context, ...args) {
let fn = Symbol("fn"); // 声明一个独有的Symbol属性, 防止fn覆盖已有属性
context = context || window; // 若没有传入this, 默认绑定window对象
context[fn] = this; // this指向调用call的对象,即我们要改变this指向的函数

let result = context[fn](...args); //重点代码,利用this指向,相当于context.caller(...args)

delete context[fn];

return result ;

};
//测试
//变更函数调用者示例
function foo(num,string) {
console.log(this.name,num,string)
}

// 测试
const obj = {
name: '托马斯'
}

foo.myCall(obj, 1,'2') // 输出'托马斯' 1 '2'

2.实现 bind

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

核心要点:

1.对于普通函数,绑定 this 指向

2.对于构造函数,要保证原函数的原型对象上的属性不能丢失

  • bind()除了this还接收其他参数,bind()返回的函数也接收参数,这两部分的参数都要传给返回的函数

  • new会改变this指向:如果bind绑定后的函数被new了,那么this指向会发生改变,指向当前函数的实例

  • 需要保留原函数在原型链上的属性和方法

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

let self = this; //谨记this表示调用bind的函数

let fBound = function () {

//this instanceof fBound为true表示构造函数的情况。如new func.bind(obj)

return self.apply(

self instanceof fBound ? self : context || window,

args.concat( Array.prototype.slice.call(arguments))

);

};

fBound.prototype = Object.create(self.prototype); //保证原函数的原型对象上的属性不丢失

return fBound;

};

////测试
const obj = { name: '托马斯' }
function foo() {
console.log(this.name)
console.log(arguments)
}

foo.myBind(obj, 'a', 'b', 'c')() //托马斯 ['a', 'b', 'c']

3.实现 new 关键字

核心要点:

  1. 创建一个全新的对象
  2. 这个对象的proto要指向构造函数的原型对象
  3. 执行构造函数
  4. 构造函数返回值为 object 类型则作为 new 方法的返回值返回,否则返回上述全新对象
1
2
3
4
5
6
7
8
9
function myNew(fn, ...args) {
// 把obj挂原型链上
let obj = Object.create(fn.prototype);
// 执行构造方法, 并为其绑定新this, 这一步是为了让构造方法能进行this.name = name之类的操作
let res = fn.apply(obj, args);

return typeof res === "object" ? res : obj;

}