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指向的函数。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐