apply,call,bind三者的区别
1、三者都可以改变函数的this对象指向。
2、三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefined或null,则默认指向全局window。
3、三者都可以传参,但是apply是数组,而call是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入。
bind 是返回绑定this之后的函数,便于稍后调用;apply 、call 则是立即执行 。
为什么要改变this指向?
来,给大爷们上栗子,
var name="lucy";
const obj={
name:"martin",
say:function () {
console.log(this.name);
}
};
obj.say(); //martin,this指向obj对象
setTimeout(obj.say,0); //lucy,this指向window对象
可以看出来,正常调用obj的话,this的指向是没有问题的,但是放到setTimeout中做回调问题就出现了,因为setTimeout因此回到主栈执行时是在全局执行上下文的环境中执行的,这时候this指向自然是window,这显然不符合我们的需求,因此便需要改变this的指向。
apply
var name="martin";
var obj={
name:"lucy",
say:function(year){
console.log(this.name+" is "+year);
}
};
var say=obj.say;
setTimeout(function(){
say.apply(obj,["18"])
} ,0); //lucy is 18,this改变指向了obj
say("20") //martin is 20,this指向window,说明apply只是临时改变一次this指向
手写实现原理:
// apply原理一致 只是第二个参数是传入的数组
Function.prototype.myApply = function (context, args) {
if (!context || context === null) {
context = window;
}
// 创造唯一的key值 作为我们构造的context内部方法名
let fn = Symbol();
context[fn] = this;
// 执行函数并返回结果
return context[fn](...args);
};
call
var name="martin";
var obj={
name:"lucy",
say:function(year,place){
console.log(this.name+" is "+year+' from '+place);
}
};
var say=obj.say;
setTimeout(function(){
say.call(obj,18,'china')
} ,0); // lucy is 18 from china,this改变指向了obj
say(20,"火星") // martin is 20 from 火星,this指向window,说明apply只是临时改变一次this指向
我们可以看到,call和apply的区别是call以参数列表的形式传入,而apply以参数数组的形式传入。
原理实现:
Function.prototype.newCall = function (context, ...args) {
if (!context || context === null) {
context = window;
}
// 创造唯一的key值 作为我们构造的context内部方法名
let fn = Symbol();
context[fn] = this; //this指向调用call的函数
// 执行函数并返回结果 相当于把自身作为传入的context的方法进行调用了
return context[fn](...args);
};
bind
var name="martin";
var obj={
name:"lucy",
say:function(year,place){
console.log(this.name+" is "+year+' from '+place);
}
};
var say=obj.say.bind(obj,18)
this.say();//lucy is 18 from undefined
this.say("火星");//lucy is 18 from 火星
原理实现:
Function.prototype.myApply = function(context) {
// 如果没有传或传的值为空对象 context指向window
if (typeof context === "undefined" || context === null) {
context = window
}
let fn = mySymbol(context)
context[fn] = this //给context添加一个方法 指向this
// 处理参数 去除第一个参数this 其它传入fn函数
let arg = [...arguments].slice(1) //[...xxx]把类数组变成数组,arguments为啥不是数组自行搜索 slice返回一个新数组
context[fn](arg) //执行fn
delete context[fn] //删除方法
}
bind方法和call很相似,第一参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入,call则必须一次性传入所有参数),但是它改变this指向后不会立即执行,而是返回一个永久改变this指向的函数。