JS修改this指向:call、apply和bind函数

94 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情 >>

call 函数

语法

obj.call(thisObj,arg[,arg2[,arg3[,...agr]]]);

简介

修改obj的this指向为thisObj,后面的参数会当成obj的参数安装顺序传递进去。

我们先来看一下下面这段代码会输出什么:

function say(arg1, arg2) {
  console.log(this.name, arg1, arg2);
}

const obj = {
  name: "tom",
  say: function () {
    console.log(this.name);
  }
};
say();

输出结果:

在这里插入图片描述

我们发现报错了,程序并没有找到name。 现在我们来改动一下,使用call函数来改变this的指向,看看会发生什么:

say.call(obj, "1", "2");

发现输出的结果变成了:

在这里插入图片描述

call把say函数的this变为了obj。

实现

下面我们来实现一个call函数

Function.prototype._call = function (context) {
  // 判断是否是一个函数
  // this指的是调用的函数
  if (typeof this !== "function") {
        throw new TypeError("Error");
  }
  context = context || window;
  context.fn = this;

  const args = [...arguments].slice(1);
  const result = context.fn(...args);

  delete context.fn;
  return result;
};

验证一下:

say._call(obj, "1", "2");

发现输出结果与期望的相同。

apply函数

语法

obj.call(thisObj[,argArray]);

简介

applycall函数的用法相同,只是参数的传递变成了数组形式。

say.apply(obj, ["1", "2"]);

输出的结果与调用call相同:

在这里插入图片描述

实现

Function.prototype._apply = function (context, args) {

  // 判断是否是一个函数

  // this指的是调用的函数

  if (typeof this !== "function") {

    throw new TypeError("Error");

  }

  context = context || window;

  context.fn = this;

  let result = null;

  if (args) {

    result = context.fn(...args);

  } else {

    result = context.fn();

  }

  delete context.fn;

  return result;

};

bind 函数

语法

obj.bind(thisObj,arg[,arg2[,arg3[,...agr]]]);

简介

bind函数会返回一个新函数,但是不会像call和apply一样立即执行函数,bind后面的参数会当成obj的参数安装顺序传递进去。

我们先来看两段代码

const s = say.bind(obj, "1", "2");

s();
const s = say.bind(obj);

s("1", "2");

发现这两段代码输出的结果是一样的。

实现

我们利用apply来实现一下bind函数:

Function.prototype._bind = function (context) {

  // 判断是否是一个函数

  if (typeof this !== "function") {

    throw new TypeError("Error");

  }

  const args = [...arguments].slice(1);

  const fn = this;

  const F = function () {},

    fBound = function () {

      return fn.apply(

        this instanceof F && context ? this : context || window,

        args.concat(...arguments)

      );

    };

  F.prototype = this.prototype;

  fBound.prototype = new F();

  return fBound;

};