前端百题斩——快速手撕Call、Apply、Bind

开发 前端
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。其返回值是使用调用者提供的this值和参数调用该函数的返回值,若该方法没有返回值,则返回undefined。

[[404584]]

在百题斩js中的这些“this”指向都值得了解中已经简要概述了call、apply、bind三个方法,这三者作用是相同的,均可以改变this指向,从而让某对象可以调用自身不具备的方法,本节将深入理解这三者的实现原理。

15.1 call()

15.1.1 基础

call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。其返回值是使用调用者提供的this值和参数调用该函数的返回值,若该方法没有返回值,则返回undefined。

基本用法:

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

小试牛刀

  1. function method(val1, val2) { 
  2.     return this.a + this.b + val1 + val2; 
  3.  
  4. const obj = { 
  5.     a: 1, 
  6.     b: 2 
  7. }; 
  8.  
  9. console.log(method.call(obj, 3, 4)); // 10 

15.1.2 实现

实现一个call函数,将通过以下几个步骤:

  1. 获取第一个参数(注意第一个参数为null或undefined时,this指向window),构建对象
  2. 将对应函数传入该对象中
  3. 获取参数并执行相应函数
  4. 删除该对象中函数,消除副作用
  5. 返回结果
  1. Function.prototype.myCall = function (context, ...args) { 
  2.     // 获取第一个参数(注意第一个参数为null或undefined时,this指向window),构建对象 
  3.     context = context ? Object(context) : window; 
  4.     // 将对应函数传入该对象中 
  5.     context.fn = this; 
  6.     // 获取参数并执行相应函数 
  7.     let result = context.fn(...args); 
  8.     // 消除副作用 
  9.     delete context.fn; 
  10.     // 返回结果 
  11.     return result; 
  12. // …… 
  13. console.log(method.myCall(obj, 3, 4)); // 10 

15.2 apply()

15.2.1 基础

apply() 方法调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。其返回值是指定this值和参数的函数的结果。call() 和 apply()的区别是call()方法接受的是参数列表,而apply()方法接受的是一个参数数组;

基本用法

  1. func.apply(thisArg, [argsArray]) 

小试牛刀

  1. function method(val1, val2) { 
  2.     return this.a + this.b + val1 + val2; 
  3.  
  4. const obj = { 
  5.     a: 1, 
  6.     b: 2 
  7. }; 
  8.  
  9. console.log(method.apply(obj, [3, 4])); // 10 

15.2.2 实现

apply和call的区别主要是参数的不同,所以其实现步骤的call大体类似,如下所示:

  1. Function.prototype.myApply = function (context, arr) { 
  2.     context = context ? Object(context) : window; 
  3.     context.fn = this; 
  4.  
  5.     let result = arr ? context.fn(...arr) : context.fun(); 
  6.  
  7.     delete context.fn; 
  8.  
  9.     return result; 
  10. // …… 
  11. console.log(method.myApply(obj, [3, 4])); // 10 

15.3 bind()

15.3.1 基础

bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。该函数的返回值是一个原函数的拷贝,并拥有指定的this值和初始参数。

基本用法

  1. function.bind(thisArg[, arg1[, arg2[, ...]]]) 

小试牛刀

  1. function method(val1, val2) { 
  2.     return this.a + this.b + val1 + val2; 
  3.  
  4. const obj = { 
  5.     a: 1, 
  6.     b: 2 
  7. }; 
  8.  
  9. const bindMethod = method.bind(obj, 3, 4); 
  10. console.log(bindMethod()); // 10 

15.3.2 实现

实现一个bind函数相对较复杂一些,应该注意以下几点:

  1. 能够改变this指向;
  2. 返回的是一个函数;
  3. 能够接受多个参数;
  4. 支持柯里化形式传参 fun(arg1)(arg2);
  5. 获取到调用bind()返回值后,若使用new调用(当做构造函数),bind()传入的上下文context失效。
  1. Function.prototype.myBind = function (context, ...args) { 
  2.     if (typeof(this) !== 'function') { 
  3.         throw new TypeError('The bound object needs to be a function'); 
  4.     } 
  5.  
  6.     const self = this; 
  7.     // 定义一个中装函数 
  8.     const fNOP = function() {}; 
  9.     const fBound = function(...fBoundArgs) { 
  10.         // 利用apply改变this指向 
  11.         // 接受多个参数+支持柯里化形式传参 
  12.         // 当返回值通过new调用时,this指向当前实例 (因为this是当前实例,实例的隐士原型上有fNOP的实例(fnop);fnop instanceof fNOP为true) 
  13.         return self.apply(this instanceof fNOP ? this : context, [...args, ...fBoundArgs]); 
  14.     } 
  15.  
  16.     // 将调用函数的原型赋值到中转函数的原型上 
  17.     if (this.prototype) { 
  18.         fNOP.prototype = this.prototype; 
  19.     } 
  20.     // 通过原型的方式继承调用函数的原型 
  21.     fBound.prototype = new fNOP(); 
  22.  
  23.     return fBound; 

本文转载自微信公众号「执鸢者」,可以通过以下二维码关注。转载本文请联系执鸢者公众号。

 

责任编辑:武晓燕 来源: 执鸢者
相关推荐

2021-06-16 07:03:37

New操作符函数

2024-03-15 08:21:17

bindJavaScrip函数

2021-12-03 06:59:23

操作符验证点属性

2021-05-09 22:00:59

TypeofInstanceof运算符

2021-10-19 22:23:05

typeof方式Instanceof

2021-06-18 07:16:17

JavaScript apply()方法call()方法

2021-12-05 08:27:56

Javascript 高阶函数前端

2021-05-30 19:02:59

变量对象上下文

2015-03-02 09:22:09

Javascript函数用法apply

2017-10-10 14:36:07

前端Javascriptapply、call、

2024-08-26 14:35:19

JavaScript关键字对象

2021-07-14 07:00:53

浏览器技巧前端

2021-10-18 09:01:01

前端赋值浅拷贝

2021-08-04 06:56:49

HTTP缓存前端

2021-11-30 06:56:58

CallApply函数

2021-06-04 07:04:29

闭包JavaScript函数

2021-05-12 07:04:55

Js变量方式

2021-11-19 09:01:09

防抖节流前端

2021-07-26 05:01:55

浏览器渲染流程

2021-06-28 07:12:28

赋值浅拷贝深拷贝
点赞
收藏

51CTO技术栈公众号