call,apply,bind应用
this的几种情况
基于call,apply,bind来改变函数中的this
-
Function.prototype->call,apply,bind所有的函数都可以调用这三个方法
- call fn先基于__proto__找到Function.prototype.call,把call方法执行的时候,call方法内部实现了一些功能,会把fn执行,并且让fn中的this变为第一个实参值
- apply的作用和细节上和call是一样的,只是传递给函数实参的方式不一样
fn.call(obj,10,20); fn.apply(obj,[10,20]);function sum(){ let arr=[].slice.call(arguments) return arr.reduce((item,result)=>item+result) } sum(1,2,3)->6重写call
let obj = { name:'xxx', age:11 }; function fn(x,y){ console.log(this); return x+y } let result = fn.call(obj,10,20) console.log(result)call实现思路:把需要执行的函数fn,和需要改变this指向的的obj关联在一起->obj.xxx=fn,此时我们只需要obj.xxx()就相当于把fn执行,而且函数中的this就是obj
let obj = { name:'xxx', age:11, fn:fn }; function fn(x,y){ console.log(this); return x+y } let result = obj.fn(10,20) console.log(result)优化
- 临时给context设置的属性不能和原始对象中的属性冲突
- 参数的处理
- context不传递或者传递null,最后要改的this都会是window
- 必须保证context都是引用数据类型值(不论传递给什么类型)
Function.prototype.call = function call(context,...params){ //this->fn 当前执行的函数 //context->obj 需要改变的this //params->[10,20]需要函数传递的实参信息 context == null ? context = window :null !/^(Object|function)$/.test(typeof context) ? context = Object(context) :null let key = Symbol('key'), result context[key] = this; let result = context[key](...params); delete context[key]; return result; }重写bind
let obj = { name:'xxx', age:11 }; function fn(x,y){ console.log(this); return x+y } document.body.click=fn.bind(obj,10,20)document.body.onclick = function(ev){ fn.call(obj,10,20,ev) }我们期望,不论是事件触发, 还是定时器到时见,执行对应的方法时,可以改变方法中的this,以及给方法传递实参信息
- 直接用下面的方法操作是不可以的:call/apply在处理时会把函数立即执行,也就是在时间绑定或者设置定时器的时候,fn就执行了,而不是等待事件触发或者定时器到时见之后再执行 '立即处理的思想‘
document.body.onclick=fn.call(obj,12,20)预先处理思想(柯里画函数)
- 绑定方法的时候先绑定一个匿名函数,事件触发或者到达时间,先把匿名函数执行,在执行匿名函数的时候,再把我们需要执行的fn执行,此时就可以基于call、apply改变this了
- bind相对于call、apply老说,并不会把函数立即执行,只是事先处理了要改变的this和参数,一切的执行还是按照原有的时间或者出发节点进行
- document.body.onclick=fn.call(obj,10,20)
let obj = { name:'xxx', age:11 }; function fn(x,y,ev){ console.log(this,ev,x,y); return x+y } document.body.onclick = function(){ //this->body fn.call(obj,10,20,ev); } setTimeout(function(){ //this->window fn.call(obj,10,20,ev); },1000)//原理:闭包“柯里化” Function.prototype.bind = function bind(context,...params){ //this->fn最后要执行的函数 //context->obj 最后要改变的this //params->[10,20] 最后要传递的参数 let that = this return function proxy(...args){ params = params.concat(args) return that.call(context,...params); } }箭头函数改变this
let obj = { name:'xxx', age:11, fn:function(){ //this->obj let that = this <!-- return function(){ //this->window //如果需要改变obj.name that.name= 'jsy' console.log(this) } --> return ()=>{ this.name = 'jsy' console.log(this) } } }; let f = obj.fn(); f()//