概念

apply call 和bind 允许为不同的对象分配和调用属于一个对象的函数/方法。同时它们可以改变函数内 this 的指向。

区别

  • apply 和 call 接收的参数形式不同

  • apply 和 call 都是直接调用函数并得到函数执行结果,而 bind 会返回待执行函数,需要再次调用

用法演示

我们先创建一个对象 parent

const parent = {
name: 'parent',
sayPerson (age, addr) {
return {
name: this.name,
age,
addr
}
}
}

显然它具有 name 属性,及方法 sayPerson,我们现在可以通过 parent.sayPerson() 来输出该对象信息。

const person = parent.sayPerson(60, 'shenzhen');
// {name: "parent", age: 60, addr: "shenzhen"}

现在我们再创建一个对象 son

const son = {
name: 'son'
}

我们现在也想得到 son 的信息,但是 son 对象没有 sayPerson 函数怎么办?借助已有的 parent 对象和 call 方法,我们可以这样写

const person = parent.sayPerson.call(son, 26, 'shenzhen');
// {name: "son", age: 26, addr: "shenzhen"}

可以看出,通过调用 call 函数,我们为 son 对象分配了 sayPerson 方法并进行调用。实现了一个对象可以调用不属于它自己的方法,并且函数内的 this 指向该对象。apply 方法的用法其实一样,只是传参有些区别

const person = parent.sayPerson.call(son, [26, 'shenzhen']);
// {name: "son", age: 26, addr: "shenzhen"}

bind 函数则不直接调用函数,而是返回待调用函数

const sayPersonFn = parent.sayPerson.bind(son, 26, 'shenzhen');

const person = sayPersonFn();
// {name: "son", age: 26, addr: "shenzhen"}

以上就是三者的使用方法和区别,下面我们来看看它们是如何实现的

实现

call的实现

实现原理就是为对象 obj 添加需要调用的方法,接着调用该方法(此时 this 指向 obj),调用过后再删除该方法

简单版

Object.prototype.callFn = function (...args) {
// 第一个参数为目标对象
const context = args[0]; args.shift(); // 为对象赋值需要调用的方法
context.fn = this; // 调用该方法
context.fn(...args); // 删除方法
delete context.fn;
}

加上返回值

Object.prototype.callFn = function (...args) {
// 第一个参数为目标对象
const context = args[0]; args.shift(); // 为对象赋值需要调用的方法
context.fn = this; // 调用该方法
const result = context.fn(...args); // 删除方法
delete context.fn; return result;
}

在测试中发现,我们调用 call,如果第一个参数是 null 或者 undefined,那么 call 将以全局 window 来调用方法,此时 this 也指向 window。如果第一个参数不是对象类型,则以空对象 {} 来调用方法。

Object.prototype.callFn = function (...args) {
// 第一个参数为目标对象
let context = args[0]; // undefined 和 null 指向 window
if (context === null || context === undefined) {
context = window;
} // 不是对象类型则创建空对象
if (typeof context !== 'object') {
context = {};
} args.shift(); // 为对象赋值需要调用的方法
context.fn = this; // 调用该方法
const result = context.fn(...args); // 删除方法
delete context.fn; return result;
}

至此,我们实现了一个完整的 call 方法。

apply的实现

既然和 call 只存在传参的区别,那我们只需要简单修改下已实现的 call 方法即可。

Object.prototype.applyFn = function (...args) {
let context = args[0]; if (context === null || context === undefined) {
context = window;
} if (typeof context !== 'object') {
context = {};
} args.shift(); context.fn = this; // 和 call 存在差异的地方
const result = context.fn(...args[0]); delete context.fn; return result;
}

bind的实现

在实现了 apply 和 call 的前提下,bind 的实现也比较简单。

Object.prototype.bindFn = function (...args) {
// 实际就是多包了层待执行函数
return () => {
return this.applyFn(args[0], (args || []).slice(1));
}
}

至于以 bind 方法返回的函数作为构造函数来创建对象会存在的问题请参考JavaScript深入之bind的模拟实现

总结

call apply bind 在工作中实际上是比较常见的函数,特别是在一些框架或库的源码中,但是经常有人会混淆它们的用法。希望大家通过此篇文章可以彻底弄清它们的作用与区别,并且知道其实现原理,知其然知其所以然。

参考


欢迎到前端学习打卡群一起学习~516913974

apply call bind的用法与实现的更多相关文章

  1. js中call、apply、bind的用法

    原文链接:http://www.cnblogs.com/xljzlw/p/3775162.html var zlw = { name: "zlw", sayHello: funct ...

  2. call、apply、bind的用法

    数组追加 //用apply拼接 var arr1=[12,'name:foo',2048]; var arr2=['Joe','Hello']; Array.prototype.push.apply( ...

  3. javascript中call()、apply()、bind()的用法理解

    一.bind的用法 第一个:obj.showInfo('arg','arg_18');中传的2个参数通过showInfo方法改变的是obj下中的name和age 第二个:obj.showInfo.bi ...

  4. JS中的call()、apply() 以及 bind()方法用法总结

    JS中的call()方法和apply()方法用法总结  : 讲解: 调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域. function add(c,d){ return thi ...

  5. javascript中call()、apply()、bind()的用法终于理解

    其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例1 obj.objAge;  //17 obj.myFun()  //小张年龄undefined 例2 shows( ...

  6. call,apply,bind的用法与区别

    1.call/apply/bind方法的来源 首先,在使用call,apply,bind方法时,我们有必要知道这三个方法究竟是来自哪里?为什么可以使用的到这三个方法? call,apply,bind这 ...

  7. (转)javascript中call()、apply()、bind()的用法

    其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例1 obj.objAge;  //17 obj.myFun()  //小张年龄undefined 例2 shows( ...

  8. <JavaScript> call()、apply()、bind() 的用法

    其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例 1 obj.objAge; obj.myFun() // 小张年龄 undefined   例 2 shows() ...

  9. call、apply、bind 的用法

    例1 obj.objAge; //17 obj.myFun() //小张年龄undefined 例2 shows() //盲僧 比较一下这两者this 的差别,第一个打印里面的this 指向obj,第 ...

  10. JS中call()、apply()、bind()的用法

    其实是一个很简单的东西,认真看十分钟就从一脸懵B 到完全 理解! 先看明白下面: 例1 obj.objAge;  //17 obj.myFun()  //小张年龄undefined 例2 shows( ...

随机推荐

  1. Daily Scrum 12/9/2015

    Done: Yandong: multi query的代码整合完成,之前采用的是将不同query的score相加的方法,目前改用将不同query的score相乘,搜索效果得到明显提升: Zhaoyan ...

  2. [整理]svn常见问题汇总

    1.’.’ is not a working copy.Can’t open file‘.svn/entries’: 系统找不到指定的路径.解答:原因是输入的访问路径不正确,如svn://192.16 ...

  3. 小白初学Java的一点点收获

    作为刚刚学习Java没有几天的小白,我想把我在学习过程中,所学习到的知识和注意事项和大家一起分享分享.在这个过程中,希望大家可以有所收获,有什么不对的地方,希望大家指出并且私信我. 首先说说第一次记事 ...

  4. C# WCF之用接口创建服务契约、部署及客户端连接

    服务契约描述了暴露给外部的类型(接口或类).服务所支持的操作.使用的消息交换模式和消息的格式.每个WCF服务必须实现至少一个服务契约.使用服务契约必须要引用命名空间System.ServiceMode ...

  5. 不使用tomcat,仅适用javaSE手写服务器--模拟登陆

    1.搭建框架 我们只是简单模拟,框架简单分三个模块 a,服务器端server包 b,servlet,根据不同的请求url,利用反射生产对应的servlet c,IO工具包,用来关闭IO流 d,编写we ...

  6. jdk1.7和jdk1.8在接口方面的改动

    1.JDK7及其之前,接口中都是抽象方法,且不能出现static方法 2.接口的变量都是public final static 全局静态常量,无变化 3.接口中可以添加非抽象方法(static),通过 ...

  7. (第六篇)vim编辑器的使用

    什么是 vim(window文本文档) Vim是从 vi 发展出来的一个文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用.简单的来说, vi 是老式的字处理器,不过功 ...

  8. ReportEventA 错误 ERROR_CRC

    如果 level 的宏弄错了,就会出现这种奇怪的现象.

  9. [转]探索 Android 内存优化方法

    前言 这篇文章的内容是我回顾和再学习 Android 内存优化的过程中整理出来的,整理的目的是让我自己对 Android 内存优化相关知识的认识更全面一些,分享的目的是希望大家也能从这些知识中得到一些 ...

  10. 一款被大厂选用的 Hexo 博客主题

    首先这是一篇自吹自擂的文章,主题是由多位非前端程序员共同开发,目前经过一年半的迭代已经到达 v1.8.0 版本,并且获得大量认可,甚至某大厂员工已经选用作为内部博客,因此我决定写这篇文章向更多人安利它 ...