Function.prototype.call会以给定的this值和逐个提供的参数调用该函数

1
2
3
4
5
6
function foo(a, b) {
console.log(this, a, b)
return a + b
}

foo.myCall({}, 1, 2)

call的实现蕴含着很多知识点

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

// 当参数ctx为undefined或者null时,默认指向全局对象
if (ctx === undefined || ctx === null) {
// js分为node环境和浏览器环境,但globalThis一定指向全局对象
ctx = globalThis
}

// 当ctx为原始类型时,转成对象
else {
// Object构造函数,如果参数是原始类型则转成对象,否则什么都不做返回参数
ctx = Object(ctx)
}

// 使用永远不可能重复的symbol确保ctx不被污染
const key = Symbol('fn')

// 通常情况下,谁调用属性,谁就是上下文
// 所以当foo调用myCall时,this就是foo,即原函数
Object.defineProperty(ctx, key, { // 属性描述符
value: this, // 保存原函数
enumerable: false // 不可枚举(不会被打印)
})

// 将foo保存到ctx(用户传入的上下文)中后
// ctx.foo运行时的this指向就是ctx了
const res = ctx[key](...args)

// 用完记得删,这个属性只是为了调用原函数
delete ctx[key]

// 有返回值时记得返回
return res
}