apply,call,bind三者的区别及实现原理

本文详细比较JavaScript中apply(), call() 和 bind() 的功能,解释它们如何改变this指向,通过实例展示其用法,并揭示bind的独特之处——返回绑定后的函数。重点在于解决回调函数this丢失的问题。
摘要由CSDN通过智能技术生成

apply,call,bind三者的区别

1、三者都可以改变函数的this对象指向。
2、三者第一个参数都是this要指向的对象,如果如果没有这个参数或参数为undefinednull,则默认指向全局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指向的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值