手写call分析
function foo(name, age) {
console.log(this, name, age);
}
foo.call({a: 1}, 'bb', 10) // {a: 1} 'bb' 10
-
foo.call() 把foo当做一个对象,然后调用foo对象中call方法,我们每个函数都有call方法,所以我们可以直接调用foo.call() 原理是 Function.prototype.call = function () {}
-
call方法的第一个参数是foo的this指向,后面的参数是foo函数的参数
-
改变this指向原理: 我们给content对象的fn属性 赋值 foo函数,然后调用content.fn(),这样foo函数中的this就指向了content
实现myCall
// 1、每个函数对象都能调用myCall函数,所以我们可以把myCall函数挂载到Function.prototype上,且有2个参数,第一个参数是this指向,第二个参数是foo函数的参数
Function.prototype.myCall = function (content, ...args) {
// 判断content是否为null或者undefined,如果是,那么this指向window,如果不是,那么this指向content(兼容 第一个参数为其他的情况,可以省略)
content = (content === null || content === undefined) ? window : Object(content)
// 3、给content对象的fn属性赋值为foo函数, 调用content.fn(),这样foo函数中的this就指向了content
content.fn = this;
content.fn(...args)
// 删除content对象的fn属性 (多余属性,删除)
delete content.fn
}
手写apply分析
function foo(name, age) {
console.log(this, name, age);
}
foo.apply({a: 1}, ['bb', 10]) // {a: 1} 'bb' 10
- call 和 apply的第一个参数都是this指向, 不同的是apply的第二个参数是一个数组,而call的后面参数是一个一个的传递,其他都相同
- 只需要把的第二个参数改成数组就可以了
实现myApply
Function.prototype.myApply = function(content, args) {
content = (content === null || content === undefined) ? window : Object(content)
content.fn = this
content.fn(...args)
delete content.fn
}
`基于myCall优化 myApply
Function.prototype.myApply = function(content, args) {
this.myCall(content, ...args) // 同 this.call(content, ...args)
}
手写bind分析
- call和bind 第一个参数都是this指向, 后面的参数都是函数的参数, 不同的是call是直接调用函数, bind是返回一个函数
- 我们只要把call中的内容 以函数的方式返回出去就好了
- 返回的函数还可以传递参数, 把args 和 newArgs 拼接起来就好了
实现myBind
Function.prototype.myBind = function(content, ...args) {
content = (content === null || content === undefined) ? window : Object(content)
return (...newArgs)=>{
content.fn = this
content.fn(...args, ...newArgs)
// delete content.fn 这里能删除吗? 不能删除,因为返回的函数可能会被多次调用,如果删除了,那么第二次调用的时候,content.fn就不存在了
}
}
let tt = foo.bind({a: 1}, 'bb')
tt(20)
tt(20)
tt(20)
tt(20)
myCall、myApply、代码优化
-
上述,已知myCall、myApply只是第一个参数后的
入参格式不同,我们可以把公共代码抽出 -
把共代码封装成exeFn函数,挂载到Funtion.prototype上
优化后的代码如下:
// 封装exeFn挂载到Funtion.prototype上
Function.prototype.exeFn = function (context, ...args) {
context = (context === null || context === undefined) ? window : Object(context)
context.fn = this;
context.fn(...args)
delete context.fn
}
// 手写myCall
Function.prototype.myCall = function (context, ...args) {
this.exeFn(context, ...args)
}
// 手写apply
Function.prototype.myCall = function (context, args) {
this.exeFn(context, ...args)
}