channelread0会被调用两次_call/apply/bind 你都会用吗?

这是terminal的第7篇原创整理

预备知识:this指向,作用域

对于bind,applycall方法,已经是前端面试官乐此不疲的考察内容了。虽然 ES6 对于this的处理已经改善很多,但是这些内容并不会很快被抛弃,不管是面试还是维护老代码,掌握它依然是必要的。

相同点-修改this

至于为什么这三个方法要放到一起对比,因为他们都有一个相同的重要作用:

  • 改变函数的执行环境,也就是this指向

通过代码简单示范:

function say() {
  console.info("name", this.name);
}
var personA = {
  name: "A",
  say,
};
var personB = { name: "B" };

say();
personA.say();
personA.say.call(personB);
personA.say.apply(personB);
personA.say.bind(personB)();

//name undefined
//name A
//name B
//name B
//name B

上面的代码,say方法直接调用,其中的this是全局对象。只有正常调用personAsay方法的时候才会输出A。如果要输出B但是personB中并没有say方法,要怎么做?

可以通过三种方式,call,apply,bind 来完成。callapply将函数内部的this改变指向,指向了对应函数的传入的第一个参数,也就是personB,这是的say方法中的this.name就是personBname。故输出B。可能你也注意到,bind函数传入personB调用之后后面还有一个调用,因为bind方法改变this的时候并不执行该函数,而是返回一个新的函数。

call/apply 与 bind

如上,bind的返回和call/apply调用并不一样,**bind调用之后是返回一个新的函数的,该函数的this是已经修改过的,指向传递进bind函数的第一个参数,如果函数需要执行的话,需要自己再额外调用一次新的函数。而callapply这两个方法在调用之后,函数是直接执行的。**这就是call/applybind方法最大的区别。

而对于callapply方法,依然存在的相同点和不同点,callapply方法的第一个参数都是调用call/apply后的函数的this指向。区别就是两者接收其他参数的方式,call方法是接受若干个参数列表,而apply方法接受的是一个数组,数组的元素是各个参数

call 方法::

function.call(thisArg, arg1, arg2, ...)

thisArg并不是必须的,不传值或者值为undefined/null的时候函数的this指向全局对象。

apply 方法:

function.apply(thisArg, [argsArray])

apply 第一个参数则是必须的,第二个参数(数组)中的元素会作为单独的参数传给函数。

callapply的返回值是改变this后的函数的返回值。

下面是一个callappply的例子:

function say(age, gender) {
  console.info("name/age/gender", this.name + "/" + age + "/" + gender);
}
var personA = {
  name: "A",
  say,
};
var personB = { name: "B" };

personA.say.call(personB, 20, "man");
personA.say.apply(personB, [24, "woman"]);

手动实现 bind/call/apply 方法

了解了call/aplly/bind方法的作用和特点,为了更好的理解,我们可以手动实现一遍

  1. call 方法,先看一个常规的call方法使用例子:
const a = { name: "xw" };
function say(gender, age) {
  const info = "name/gender/age:" + this.name + "/" + gender + "/" + age;
  console.info(info);
  return "返回值:" + info;
}
const data = say.call(a, "man", 20);
console.info(data);

//name/gender/age:xw/man/20
//data 返回值:name/gender/age:xw/man/20

say通过call调用,say自身可以接受两个参数,用于输出,并且具有返回值。可以像这样实现:

Function.prototype.MyCall = function (context) {
  var env = context || window; //如果没有传入就指向全局
  env.fun = this; // this就是调用Mycall的函数,将其挂在env上。运行时就是say函数
  var args = [...arguments].slice(1); //获取第二个开始的参数
  var result = env.fun(...args); // 执行函数并传参,相当于say(...args)
  delete env.fn;
  return result;
};

实现完之后调用一下:

const data = say.MyCall(a, "man", 20); //name/gender/age:xw/man/20
console.info("data", data); // name/gender/age:xw/man/20

执行输出和返回值和上面的call方法调用无异。

  1. apply 方法。apply的实现和call很相似,只是参数的格式是数组。
Function.prototype.MyApply = function (context) {
  var env = context || window;
  env.fun = this;
  var result;
  if (arguments[1]) { //需要对第二个参数(数组)用到展开,加一层判断
    result = env.fun(...arguments[1]);
  } else {
    result = env.fun();
  }
  delete env.fn;
  return result;
};

//调用
const data = say.MyApply(a, ["man", 20]);
console.info("data", data);

结果和上面一样,且在不传其他参数的时候正常执行。

  1. bind 方法。区别于applycall会直接执行函数,bind则是会返回函数的引用,在传参上还有一个区别,就是**bind是支持柯里化形式传参的**。

柯里化: 将接受多个参数的函数转换成接受一个单一参数的函数,该函数返回一个接受剩余参数且返回结果的新函数。

也就是说,如果一个函数的调用传参是 funtionA(a,b),那么柯里化之后的函数 functionB,应该这样调用 functionB(a)(b),其效果不变

Function.prototype.MyBind = function (context) {
  var inThis = this;
  var args = [...arguments].slice(1);
  return function fun() {
    if (this instanceof fun) {     //区分是否构造函数
      return new inThis(...args, ...arguments); // 支持柯里化,两次获取参数
    } else {
      return inThis.apply(context, args.concat(...arguments)); 
    }
  };
};

然后进行调用测试,也是符合预期的:

const a = { name: "xw" };
function say() {
  console.info("name is", this.name);
}
say.MyBind(a)();
// name is xw

总结

call,apply 和 bind 都用来改变函数的this指向,指向call/apply/bind函数的第一个参数,其中call/apply调用时会直接执行该函数,而bind并不会直接运行函数,而是返回一个新的函数,需要手动调用才会执行。apply接受的其他参数是一个数组,而call函数是直接传多个参数的(一个参数列表)。


以上便是对 bind/call/apply 方法的使用总结,觉得不错的给点个赞吧~(关注即送前端资源包) 2ed2132b9371d7bcde0c897463fdc6af.png

推荐阅读:

关于 this 指向的问题ES6 系列(五):Generator 和迭代协议

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值