众所周知,call方法是用来改变函数(方法)的this指向,并且返回函数调用的返回值,下面将详细讲解call方法的重写,并且优化。
知道Function.prototype.call()的作用之后,我们可以使用JavaScript基础知识来重写
1. 首先call方法是Function的公用方法,并且可以传入多个参数,第一个参数是改变的this指向,剩下的参数是函数被调用所传入的参数;
// 此处第二个参数用到es6的展开语法
Function.prototype.MyCall = function (point, ...arg) {}
2. 函数的this指向是指向调用它的“对象”,所以MyCall方法中的this就是调用MyCall方法的函数
Function.prototype.MyCall = function (point, ...arg) {
console.log(this) // --->ƒ fn () {}
}
functionfn () {}
fn.MyCall()
3. 实现call方法自动调用函数并且返回调用结果
Function.prototype.MyCall = function (point, ...arg) {
const result = this(...arg) // 此时的this就是调用MyCall方法的函数,所以this()就是调用fn函数
return result
}
function fn (...arg) {
let res = [...arg]
return res
}
fn.MyCall({},1,2,3)
函数运行结果:
4. 此时我们需要考虑给函数更改this指向
同第二步一样,需要将fn函数的this指向“什么”,就用“什么”去调用fn函数
Function.prototype.MyCall = function (point, ...arg) {
// 将fn函数挂载到需要改变的this指向对象上的随便一个属性上(下面用的是"key")
point.key = this// 然后调用函数去改变this指向到“point”对象上
const result = point.key(...arg)
// 删除无用的属性
delete point.key
return result
}
function fn (...arg) {
console.log(this)
let res = [...arg]
return res
}
fn.MyCall({},1,2,3)
调用结果: 可以看到fn函数中打印的this已经指向了一个对象。
5. 优化可修改的this指向(MyCall方法的第一个参数)
当我们再给第一个参数传入null 或者 undefined时,call方法会将this指向改为全局,否则会指向一个相应的数据类型
Function.prototype.MyCall = function (point, ...arg) {
// 判断point类型// globalThis 表示全局对象 因为JavaScript运行环境不同,不可以写window。
point = point === null || point === undefined ? globalThis : Object(point)
point.key = this
const result = point.key(...arg)
delete point.key
return result
}
function fn (...arg) {
console.log(this)
let res = [...arg]
return res
}
fn.MyCall({},1,2,3)
6. 优化自定义属性“key”
上文中我们的随机属性命名为“key”,为了防止属性名重复我们也可以设置一个比较“变态的属性名” 例如:"xxx"、"aaa"等等此时我们可以用到es6中的symbol来创建一个唯一的属性名
Function.prototype.MyCall = function (point, ...arg) {
point = point === null || point === undefined ? globalThis : Object(point)
const key = Symbol("key")
point[key] = this
const result = point[key](...arg)
delete point[key]
return result
}
此时,MyCall方法在不通过apply或者bind辅助时实现了。