call,apply,bind解决的是什么问题??
答:改变函数执行时的上下文中的this指向问题
call和apply可以立即执行,而bind就是在调用的时候执行而且还可以在调用时传入参数,如:
1.let fn = person.bind(obj,'a','b');
fn('c');
2.let fn = person.bind(obj,'a');
fn('b','c');
3.let fn = person.bind(obj);
fn('a','b','c');
废话不多说先一一上图,详细讲bind
let person = {
fullName: function (address,job) {
return this.name + "|" + this.age+ "|" +address+ "|" +job;
}
}
let person2 = {
name:"张三",
age: "17",
}
call
Function.prototype.myCall = function(ctx,...argu){ //上下文初始默认window
if(typeof this !== 'function'){//调用者是函数判断
throw new TypeError('type Error')
}
ctx = ctx || window
console.log(this)
ctx.fn = this //可以为形参对象加上一个函数fn,名字自定义,结合打印出来的this,相当于给person2这个对象添加了一个fullName函数
const result = ctx.fn(...argu)// 然后接收传过来的参数
delete ctx.fn //不能改写对象,需要删除
return result
}
console.log('call',person.fullName.call(null,'上海','程学'))
console.log('mycall',person.fullName.myCall(null,'上海','程学'))
person.fullName.myCall(person2,'上海','程学')
Function.prototype.myCall = function(ctx=window,...argu){ //上下文初始默认window
if(typeof this !== 'function'){//调用者是函数判断
throw new TypeError('type Error')
}
console.log(this)
ctx.fn = this //可以为形参对象加上一个函数fn,名字自定义,结合打印出来的this,相当于给person2这个对象添加了一个fullName函数
const result = ctx.fn(...argu)// 然后接收传过来的参数
delete ctx.fn //不能改写对象,需要删除
return result
}
console.log('call',person.fullName.call(null,'上海','程学'))
console.log('mycall',person.fullName.myCall(null,'上海','程学'))
这两者有什么不同的地方?
后者打印会有Uncaught TypeError: Cannot set properties of null (setting 'fn')错误
默认window会被传入的null覆盖掉
最终版
Function.prototype.myCall = function(ctx,...argu){ //上下文初始默认window
if(typeof this !== 'function'){
throw new TypeError('type Error')
}
ctx = ctx || window
const fn = Symbol('fn')//唯一属性
ctx[fn] = this
const result = ctx[fn](...argu)
delete ctx[fn]
return result
}
apply
Function.prototype.myApply = function(ctx,argu){ //上下文初始默认window,接收一个集合参
if(typeof this !== 'function'){
throw new TypeError('type Error')
}
if(!(argu instanceof Array)){//当第2个参数不是数组的时候抛出错误
throw TypeError("CreateListFromArrayLike called on non-object");
}
ctx = ctx || window
const fn = Symbol('fn')//唯一属性
ctx[fn] = this
const result = ctx[fn](...argu)
delete ctx[fn]
return result
}
// console.log(person.fullName.myApply(person2,'上海','程学')); //CreateListFromArrayLike called on non-object
console.log(person.fullName.myApply(person2,['上海','程学']));
与call不同的就是第二个参数接收的是一个集合
bind
Function.prototype.myBind = function(ctx,...argu){
if(typeof this !== 'function'){
throw new TypeError('type Error')
}
console.log(this)
let self = this //第二步 因为返回的是一个函数,这里是赋值保存this,防止丢失
//第一步 首先考虑返回的是一个新函数
return function F(){ //会创建一个新函数,当函数被调用时,bind的第一个参数指向this。
//第三步 进行绑定
// if(self instanceof F ){ //第四步 是否是实例对象,这个时候指定的this值会失效
// return new self(...argu,...arguments) // 把调用者函数的原型对象.prototype继承赋值给到new出来的实例对象上,使之调用者函数和实例函数串联起来
// }else{
// return self.apply(ctx,[...argu,...arguments]) //bind算是类柯里化传参,所以还需要接收arguments之后的形参
// }
//简化
return (self instanceof F) ? new self(...argu,...arguments) : self.apply(ctx,[...argu,...arguments])
}
}
// person.fullName.myBind(person2)
console.log(person.fullName.myBind(person2)());
总结:call,apply,bind功能如此相似,致其原理,一通百通,相互转化。 什么时候该用,什么时候该用那种?自己去思考吧
下一期 new实现问题