apply call bind方法原理及使用场景示例详解

 更新时间:2023年08月30日 08:48:44   作者:codeteenager  
这篇文章主要为大家介绍了apply&call&bind方法原理及使用场景示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

正文

call、apply 和 bind 是挂在 Function 对象上的三个方法,调用这三个方法的必须是一个函数。

基本用法如下:

func.call(thisArg, param1, param2, ...)
func.apply(thisArg, [param1,param2,...])
func.bind(thisArg, param1, param2, ...)

其中 func 是要调用的函数,thisArg 一般为 this 所指向的对象,后面的 param1、2 为函数 func 的多个参数,如果 func 不需要参数,则后面的 param1、2 可以不写。

这三个方法共有的、比较明显的作用就是,都可以改变函数 func 的 this 指向。call 和 apply 的区别在于,传参的写法不同:apply 的第 2 个参数为数组; call 则是从第 2 个至第 N 个都是给 func 的传参;而 bind 和这两个(call、apply)又不同,bind 虽然改变了 func 的 this 指向,但不是马上执行,而这两个(call、apply)是在改变了函数的 this 指向之后立马执行。

这三个方法的理念都是借用方法的思路,例如A 对象有个 getName 的方法,B 对象也需要临时使用同样的方法,那么这时候我们是单独为 B 对象扩展一个方法,还是借用一下 A 对象的方法呢?当然是可以借用 A 对象的 getName 方法,既达到了目的,又节省重复定义,节约内存空间。

方法/特征callapplybind
方法参数多个单个数组多个
方法功能函数调用改变this函数调用改变this函数调用改变this
返回结果的直接执行的直接执行返回待执行函数
底层实现通过eval通过eval间接调用apply

应用场景

我们来看看应用场景有哪些?

判断数据类型

用 Object.prototype.toString 来判断类型是最合适的,借用它我们几乎可以判断所有类型的数据。

function getType(obj){
  let type  = typeof obj;
  if (type !== "object") {
    return type;
  }
  return Object.prototype.toString.call(obj).replace(/^$/, '$1');
}

判断数据类型就是借用了 Object 的原型链上的 toString 方法,最后返回用来判断传入的 obj 的字符串,来确定最后的数据类型。

类数组借用方法

类数组因为不是真正的数组,所有没有数组类型上自带的种种方法,所以我们就可以利用一些方法去借用数组的方法,例如:

var arrayLike = { 
  0: 'java',
  1: 'script',
  length: 2
} 
Array.prototype.push.call(arrayLike, 'jack', 'lily'); 
console.log(typeof arrayLike); // 'object'
console.log(arrayLike);
// {0: "java", 1: "script", 2: "jack", 3: "lily", length: 4}

arrayLike 是一个对象,模拟数组的一个类数组。从数据类型上看,它是一个对象。从上面的代码中可以看出,用 typeof 来判断输出的是 'object',它自身是不会有数组的 push 方法的,这里我们就用 call 的方法来借用 Array 原型链上的 push 方法,可以实现一个类数组的 push 方法,给 arrayLike 添加新的元素。

获取数组的最大/最小值

我们可以用 apply 来实现数组中判断最大 / 最小值,apply 直接传递数组作为调用方法的参数,也可以减少一步展开数组,可以直接使用 Math.max、Math.min 来获取数组的最大值 / 最小值,例如:

let arr = [13, 6, 10, 11, 16];
const max = Math.max.apply(Math, arr); 
const min = Math.min.apply(Math, arr);
console.log(max);  // 16
console.log(min);  // 6

apply和call的实现

apply 和 call 基本原理是差不多的,只是参数存在区别。

Function.prototype.call = function (context, ...args) {
  var context = context || window;
  context.fn = this;
  var result = eval('context.fn(...args)');
  delete context.fn
  return result;
}
Function.prototype.apply = function (context, args) {
  let context = context || window;
  context.fn = this;
  let result = eval('context.fn(...args)');
  delete context.fn
  return result;
}

实现 call 和 apply 的关键就在 eval 这行代码。其中显示了用 context 这个临时变量来指定上下文,然后还是通过执行 eval 来执行 context.fn 这个函数,最后返回 result。

要注意这两个方法和 bind 的区别就在于,这两个方法是直接返回执行结果,而 bind 方法是返回一个函数,因此这里直接用 eval 执行得到结果。

bind的实现

bind 的实现思路基本和 apply 一样,但是在最后实现返回结果这里,bind 和 apply 有着比较大的差异,bind 不需要直接执行,因此不再需要用 eval ,而是需要通过返回一个函数的方式将结果返回,之后再通过执行这个结果,得到想要的执行效果。

Function.prototype.bind = function (context, ...args) {
    if (typeof this !== "function") {
      throw new Error("this must be a function");
    }
    var self = this;
    var fbound = function () {
        self.apply(this instanceof self ? this : context, args.concat(Array.prototype.slice.call(arguments)));
    }
    if(this.prototype) {
      fbound.prototype = Object.create(this.prototype);
    }
    return fbound;
}

实现 bind 的核心在于返回的时候需要返回一个函数,故这里的 fbound 需要返回,但是在返回的过程中原型链对象上的属性不能丢失。因此这里需要用Object.create 方法,将 this.prototype 上面的属性挂到 fbound 的原型上面,最后再返回 fbound。这样调用 bind 方法接收到函数的对象,再通过执行接收的函数,即可得到想要的结果。

以上就是apply call bind方法原理及使用场景示例详解的详细内容,更多关于apply call bind原理的资料请关注脚本之家其它相关文章!

相关文章

  • 微信小程序 教程之条件渲染

    微信小程序 教程之条件渲染

    这篇文章主要介绍了微信小程序 条件渲染的相关资料,并附简单实例代码,需要的朋友可以参考下
    2016-10-10
  • 用Move.js配合创建CSS3动画的入门指引

    用Move.js配合创建CSS3动画的入门指引

    这篇文章主要介绍了用Move.js配合创建CSS3动画的入门指引,文中介绍了这个JavaScript库中的一些基本方法的使用,需要的朋友可以参考下
    2015-07-07
  • Web Worker线程解决方案electron踩坑记录

    Web Worker线程解决方案electron踩坑记录

    这篇文章主要为大家介绍了Web Worker线程解决方案electron踩坑记录,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 微信小程序 devtool隐藏的秘密

    微信小程序 devtool隐藏的秘密

    这篇文章主要介绍了微信小程序 devtool隐藏的秘密的相关资料,需要的朋友可以参考下
    2017-01-01
  • 微信小程序 简单DEMO布局,逻辑,样式的练习

    微信小程序 简单DEMO布局,逻辑,样式的练习

    这篇文章主要介绍了微信小程序 简单DEMO布局,逻辑,样式的练习的相关资料,这里写一个简单实例练习小程序的布局,并附实例代码和实现效果图,需要的朋友可以参考下
    2016-11-11
  • js c++ vue方法与数据交互通信示例

    js c++ vue方法与数据交互通信示例

    这篇文章主要为大家介绍了js c++ vue方法与数据交互通信示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • mitt tiny-emitter发布订阅应用场景源码解析

    mitt tiny-emitter发布订阅应用场景源码解析

    这篇文章主要为大家介绍了mitt tiny-emitter发布订阅应用场景源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • 一款功能强大的markdown编辑器tui.editor使用示例详解

    一款功能强大的markdown编辑器tui.editor使用示例详解

    这篇文章主要为大家介绍了一款功能强大的markdown编辑器tui.editor使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • 浅谈TypeScript 索引签名的理解

    浅谈TypeScript 索引签名的理解

    这篇文章主要给大家分享的是TypeScript 索引签名的理解,索引签名由方括号中的索引名称及其类型组成,后面是冒号和值类型:{ [indexName: KeyType]: ValueType }, KeyType 可以是一个 string、number 或 symbol,而ValueType 可以是任何类型,下面就俩简单了解一下吧
    2021-10-10
  • 微信小程序通过api接口将json数据展现到小程序示例

    微信小程序通过api接口将json数据展现到小程序示例

    这篇文章主要介绍了微信小程序通过api接口将json数据展现到小程序示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01

最新评论