实现call函数

思路

原理:函数作为对象的属性调用时,this指向对象

  • 获取thisArg参数和剩余参数
  • 获取调用call的函数,把该函数设置到thisArg.func属性上
  • 执行thisArg.func函数,并接收函数执行后的结果result
  • 移除thisArg上的func函数
  • 返回执行结果

源码

1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.myCall = function(thisArg, ...args) {
// 值为 null, undefined时, 自动指向全局对象window
thisArg = thisArg == null ? window : thisArg
// 获取调用myCall时的方法,并把函数设置到thisArg.func属性上
thisArg.func = this
// 执行thisArg.func函数,接收结果
const result = thisArg.func(...args)
// 移除thisArg上的func
delete thisArg.func
// 返回结果
return result
}

测试

1
2
3
4
5
6
7
8
9
function sayHello(time) {
console.log(`${this.name} say ${this.word} at ${time}`);
}
const bottle = {
name: 'violet-mio',
word: 'hello'
}
// myCall把sayHello中this指向变为bottle对象,可以获取到bottle上的属性
sayHello.myCall(bottle, new Date().toLocaleTimeString()) // violet-mio say hello at 下午4:46:28

手写bind函数

思路

  • 获取thisArg剩余参数args
  • 获取调用myBind的函数,保存到func
  • 新建一个返回函数resultFn,在resultFn中做如下逻辑
    • 获取执行resultFn时的secondArgs, 把之前的参数args和secondArgs做合并
    • 判断是否使用new关键字执行resultFn函数
      • 如果是,resultFn函数内部this不变
      • 否则,resultFn函数内部this修改为thisArg
    • 通过apply执行func函数,并返回执行结果
    • 修改resultFn函数的原型,绑定到Object.create(func.prototype)

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Function.prototype.myBind = function(thisArg, ...args) {
thisArg = thisArg == null ? window : thisArg
// 保存调用myBind的函数
const func = this

const resultFn = function(...secondArgs) {
// 合并参数
const fullArgs = args.concat(secondArgs)
// 如果是通过 new 关键字调用的,绑定 this 为实例对象
const realThis = this instanceof resultFn ?
this : thisArg
return func.apply(realThis, fullArgs)
}

// 绑定原型
resultFn.prototype = Object.create(func.prototype)
return resultFn
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Person(name, age) {
console.log('函数调用')
// 如果是new 执行的,实例对象的__proto__属性会指向构造函数的prototype
console.log('是否使用new', this.__proto__ === Person.prototype)
this.name = name
this.age = age
}
Person.prototype.sayHi = function() {
console.log('sayHi函数执行');
console.log(this.name, this.age);
}
const obj = {
name: '我是obj传进来的name',
age: '我是obj传进来的age'
}

const Student = Person.bind(obj, '实际参数name')
// 通过new创建Point执行bind函数后返回值的实例,并为构造函数传入参数
const stu = new Student('实际参数age')
stu.sayHi()
普通函数调用
const teaher = Person.bind(obj, '实际参数name')
teaher()