理解 call、apply、bind 原理,手写简单的 call、apply、bind 方法

理解 call、apply、bind 原理,手写简单的 call、apply、bind 方法

call 原理及实现

MDN定义:call()方法使用给定的 this 值和单独提供的参数调用函数。

用自己的话说就是:改变调用 call 方法的 this 指向。

使用语法:function.call(target, arg1, arg2, arg3)

  • 上菜说明:
// 全局的name
var name = "全局的name";

// obj对象
const obj = {
  name: "obj对象的name",
};

// 方法
function printName(a) {
  console.log(this.name + a);
}

printName(); // 直接调用this指向全局
// 全局的name

printName.call(obj, "附加"); // 改变this指向为obj
// obj对象的name附加

理解了 call 是做什么事情的,接下来根据理解的思路自己写一个 myCall 方法出来。

须知:1. call 实际上是 Function.prototype 上面的方法;2. 任何函数都可以访问自己的 prototype 上面的方法

  • 上 DIY 的菜:
Function.prototype.myCall = function (targetObj = {}, ...args) {
  const fn = Symbol("fn"); // 定义一个唯一的 Symbol 避免意外污染替换 targetObj 的属性
  targetObj[fn] = this; // this 就是调用 myCall 方法的“函数本身”
  const result = targetObj[fn](...args); // 通过 targetObj 执行方法,并传入额外参数
  delete targetObj[fn]; // 删除添加的属性
  return result; // 返回执行结果
};

const obj = {
  name: "alice",
};
// 方法
function printName(a) {
  console.log(this.name + a);
}
printName.myCall(obj, " is a beauty");
// alice is a beauty

搞定!根据思路自己写出来后是不是很 nice,感觉很简单


apply 原理及实现

apply 的效果与 call 功效相同,都是改变 this 指向的问题。不同点:apply 接收的参数是目标对象、参数数组两个参数

  • 直接 DIY 菜
Function.prototype.myApply = function (target, argList) {
  const targetObj = target || {};
  const fn = Symbol("fn");
  targetObj[fn] = this;
  const result = targetObj[fn](...argList);
  delete targetObj[fn];
  return result;
};

// 测试一下
const obj = {
  name: "crk",
};
function print(...arr) {
  console.log(this.name + arr);
}

print.myApply(obj, [1, 2, 3]);

bind 的实现与原理

用法与前二者类似,被指定 this 指向后不立即执行,会返回一个可以执行的新函数,不会自动执行。

  • 注意点:
    1、bind() 除了 this 外还接收其他参数,其返回的函数也可以接收参数(意思是执行返回的函数的时候可以传参)。并且这两部分参数都需要传给返回的函数
    2、bind() 返回的方法如果被 new 执行了,那么内部的 this 指向又会被更改
    3、需要保留“元方法”在原型链上的属性和方法

简单的 bind 实现:

Function.prototype.myBind = function () {
  const args = Array.prototype.slice.call(arguments); // myBind 接口的所有参数
  const targetObj = args.shift(); // 第一个参数是 this 的目标对象,args剩下的是绑定时的参数
  const self = this; // 调用 myBind 的"元方法"

  // new 优先改变 this
  const cbFn = function () {
    // 当是new的时候,this instanceof self 的值为true,这个时候this应该指向被new的函数自己
    self.apply(
      this instanceof self ? this : targetObj,
      args.concat(Array.prototype.slice.call(arguments))
    );
  };

  // 被bind后生产的函数可以多次调用,是持久的。所以需要保留原型链上的方法和属性。这里使用原型继承
  cbFn.prototype = Object.create(self.prototype);

  return cbFn;
};

测试一下基础功能:

const obj = {
  name: "小强",
};

function foo() {
  console.log(this.name, ...arguments);
}

foo.myBind(obj, 1, 2, 3)(); // 小强 1 2 3
foo.myBind(obj, 1, 2, 3)(4); // 小强 1 2 3 4

测试bind之后的函数 new 的功能

function factory(name) {
  this.name = name;
  this.fn = function () {
    return this.name;
  };
}

const newObj = {
  name: "大强",
};

const bindF = factory.myBind(newObj);
const f = new bindF("大枪");
console.log(f.fn()); // 大枪
  • 虽然myBind的时候指定了this,但是new的使用改变了this的指向

至此,毕

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值